Интернет-журнал "Домашняя лаборатория", 2007 №9
Шрифт:
public void TestStacks
{
OneLinkStack<int> stackl = new OneLinkStack<int> ;
OneLinkStack<string> stack2 = new OneLinkStack<string>;
ArrayUpStack<double> stack3 = new ArrayUpStack
<double>(10);
stack1.put (11); stackl.put (22);
int x1 = stackl.item, x2 = stackl.item;
if ((x1 == x2) && (xl == 22)) Console.WriteLine("OK!");
stack1.remove; x2 = stack1.item;
if ((x1!= x2) && (x2 == 11)) Console.WriteLine("OK!");
stack1.remove; x2 = (stack1.empty)? 77: stackl.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);
}
В
Рис. 22.3. Три разных стека, порожденных абстрактным универсальным классом
Дополним наше рассмотрение еще одним примером работы с вариацией стеков, в том числе хранящим Объекты класса Person;
public void TestPerson
{
OneLinkStack<int> stack1 = new OneLinkStack<int> ;
OneLinkStack<string> stack2 = new OneLinkStack<string> ;
ArrayUpStack<double> stack3 = new ArrayUpStack <double> (10);
ArrayUpStack<Person> stack4 = new ArrayUpStack<Person>(7);
stack2.put("Петров"); stack2.put("Васильев");
stack2.put("Шустов");
stack1.put (27); stack1.put (45);
stack1.put (53); stack3.put (21550.5); stack3.put (12345.7);
stack3.put (32 458.8);
stack4.put(new Person(stack2.item, stack1.item,
stack3.item));
stack1.remove; stack2.remove; stack3.remove;
stack4.put(new Person(stack2.item, stack1.item,
stack3.item));
stack1.remove; stack2.remove; stack3.remove;
stack4.put(new Person(stack2.item, stack1.item,
stack3.item));
Person pers = stack4.item; pers.PrintPerson ;
stack4.remove; pers = stack4.item; pers.PrintPerson;
stack4.remove; pers = stack4.item; pers.PrintPerson;
stack4.remove; if (stack4.empty) Console.WriteLine("OK!");
}
Результаты работы этой процедуры приведены на рис. 22.4.
Рис. 22.4. Работа со стеками
Ограниченная универсальность
Хорошо, когда есть свобода. Еще лучше, когда свобода ограничена. Аналогичная ситуация имеет место и с универсальностью. Универсальность следует ограничивать. На типы универсального класса, являющиеся его параметрами, следует накладывать ограничения. Звучит парадоксально, но, наложив ограничения на типы, программист получает гораздо большую свободу в работе с объектами этих типов.
Если немного подумать, то это совершенно естественная ситуация. Когда имеет место неограниченная универсальность, над объектами типов можно выполнять только те операции, которые допускают все типы, — в C# это эквивалентно операциям, разрешенным
В языке C# допускаются три вида ограничений, накладываемых на родовые параметры.
• Ограничение наследования. Это основный вид ограничений, указывающий, что тип T является наследником некоторого класса и ряда интерфейсов. Следовательно, над объектами типа T можно выполнять все операции, заданные базовым классом и интерфейсами. Эти операции статический контроль типов будет разрешать и обеспечивать для них интеллектуальную поддержку, показывая список разрешенных операций. Ограничение наследования позволяет выполнять над объектами больше операций, чем в случае неограниченной универсальности. Синтаксически ограничение выглядит так: where Т: BaseClass, I1…Ik.
• Ограничение конструктора. Это ограничение указывает, что тип T имеет конструктор без аргументов и, следовательно, позволяет создавать объекты типа T. Синтаксически ограничение выглядит так: where Т: new.
• Ограничение value /reference. Это ограничение указывает, к значимым или к ссылочным типам относится тип T. Для указания значимого типа задается слово struct, для ссылочных — class. Так что синтаксически этот тип ограничений выглядит так: where T: struct.
Возникает законный вопрос: насколько полна предлагаемая система ограничений? Конечно, речь идет о практической полноте, а не о математически строгих определениях. С позиций практики систему хотелось бы дополнить, в первую очередь, введением ограничений операций, указывающим допустимые знаки операций в выражениях над объектами соответствующего типа. Хотелось бы, например, указать, что к объектам типа т применима операция сложения + или операция сравнения <. Позже я покажу, как можно справиться с этой проблемой, но предлагаемое решение довольно сложно. Наличие ограничения операций намного элегантнее решало бы эту проблему.