T[] stack; int top; /// <summary> /// конструктор /// </summary> /// <param name="size">размер стека</param> public ArrayUpStack(int size) { SizeOfStack = size; stack = new T[SizeOfStack]; top = 0; } /// <summary> /// require: (top < SizeOfStack) /// </summary> /// <param name="x"> элемент, помещаемый в стек</param> public override void put(T x) { stack[top] = x; top++; } public override void remove() { top--; } public override T item() { return (stack[top-1]); } public override bool empty() { return (top == 0); } }//class ArrayUpStack Созданные в результате наследования классы-потомки перестали быть абстрактными, но все еще остаются универсальными. На третьем этапе порождаются конкретные экземпляры потомков - универсальных классов, в этот момент и происходит конкретизация типов, и два экземпляра одного универсального класса могут работать с данными различных типов. Этот процесс создания экземпляров с подстановкой конкретных типов называют родовым порождением экземпляров. Вот как в тестирующей процедуре создаются экземпляры созданных нами классов: public void TestStacks() { OneLinkStack<int> stack1 = new OneLinkStack<int>(); OneLinkStack<string> stack2 = new OneLinkStack<string>(); ArrayUpStack<double> stack3 = new ArrayUpStack <double>(10); stack1.put(11); stack1.put(22); int x1 = stack1.item(), x2 = stack1.item(); if ((x1 == x2) && (x1 == 22)) Console.WriteLine("OK!"); stack1.remove(); x2 = stack1.item(); if ((x1 != x2) && (x2 == 11)) Console.WriteLine("OK!"); stack1.remove(); x2 = (stack1.empty())? 77 : stack1.item(); if ((x1 != x2) && (x2 == 77)) Console.WriteLine("OK!"); stack2.put("first"); stack2.put("second"); stack2.remove(); string s = stack2.item(); if (!stack2.empty()) Console.WriteLine(s); stack3.put(3.33); stack3.put(Math.Sqrt(Math.PI)); double res = stack3.item(); stack3.remove(); res += stack3.item(); Console.WriteLine("res= {0}", res); } В трех первых строках этой процедуры порождаются три экземпляра стеков. Все они имеют общего родителя - абстрактный универсальный класс GenStack |