Чтение онлайн

на главную - закладки

Жанры

Интернет-журнал "Домашняя лаборатория", 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);

}

В

трех первых строках этой процедуры порождаются три экземпляра стеков. Все они имеют общего родителя — абстрактный универсальный класс GenStack, но каждый из них работает с данными своего типа и по-разному реализует методы родителя. На рис. 22.3 показаны результаты работы этой процедуры.

Рис. 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# это эквивалентно операциям, разрешенным

над объектами типа object, прародителя всех типов. В нашем предыдущем примере, где речь шла о свопинге, над объектами выполнялась единственная операция присваивания. Поскольку присваивание внутри одного типа разрешено для всех типов, то неограниченная универсальность приемлема в такой ситуации. Но что произойдет, если попытаться выполнить сложение элементов, сравнение их или даже простую проверку элементов на равенство? Немедленно возникнет ошибка еще на этапе компиляции. Эти операции не разрешены для всех типов, поэтому в случае компиляции такого проекта ошибка могла бы возникнуть на этапе выполнения, когда вместо формального типа появился бы тип конкретный, не допускающий подобную операцию. Нельзя ради универсальности пожертвовать одним из важнейших механизмов C# и Framework.Net — безопасностью типов, поддерживаемой статическим контролем типов. Именно поэтому неограниченная универсальность существенно ограничена. Ее ограничивает статический контроль типов. Бывают, разумеется, ситуации, когда необходимо на типы наложить ограничения, позволяющие ослабить границы статического контроля. На практике универсальность почти всегда ограничивается, что, повторяю, дает большую свободу программисту.

В языке C# допускаются три вида ограничений, накладываемых на родовые параметры.

Ограничение наследования. Это основный вид ограничений, указывающий, что тип T является наследником некоторого класса и ряда интерфейсов. Следовательно, над объектами типа T можно выполнять все операции, заданные базовым классом и интерфейсами. Эти операции статический контроль типов будет разрешать и обеспечивать для них интеллектуальную поддержку, показывая список разрешенных операций. Ограничение наследования позволяет выполнять над объектами больше операций, чем в случае неограниченной универсальности. Синтаксически ограничение выглядит так: where Т: BaseClass, I1…Ik.

Ограничение конструктора. Это ограничение указывает, что тип T имеет конструктор без аргументов и, следовательно, позволяет создавать объекты типа T. Синтаксически ограничение выглядит так: where Т: new.

Ограничение value /reference. Это ограничение указывает, к значимым или к ссылочным типам относится тип T. Для указания значимого типа задается слово struct, для ссылочных — class. Так что синтаксически этот тип ограничений выглядит так: where T: struct.

Возникает законный вопрос: насколько полна предлагаемая система ограничений? Конечно, речь идет о практической полноте, а не о математически строгих определениях. С позиций практики систему хотелось бы дополнить, в первую очередь, введением ограничений операций, указывающим допустимые знаки операций в выражениях над объектами соответствующего типа. Хотелось бы, например, указать, что к объектам типа т применима операция сложения + или операция сравнения <. Позже я покажу, как можно справиться с этой проблемой, но предлагаемое решение довольно сложно. Наличие ограничения операций намного элегантнее решало бы эту проблему.

Поделиться:
Популярные книги

Блуждающие огни

Панченко Андрей Алексеевич
1. Блуждающие огни
Фантастика:
боевая фантастика
космическая фантастика
попаданцы
5.00
рейтинг книги
Блуждающие огни

Сумеречный стрелок

Карелин Сергей Витальевич
1. Сумеречный стрелок
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Сумеречный стрелок

Он тебя не любит(?)

Тоцка Тала
Любовные романы:
современные любовные романы
7.46
рейтинг книги
Он тебя не любит(?)

В семье не без подвоха

Жукова Юлия Борисовна
3. Замуж с осложнениями
Фантастика:
социально-философская фантастика
космическая фантастика
юмористическое фэнтези
9.36
рейтинг книги
В семье не без подвоха

Холодный ветер перемен

Иванов Дмитрий
7. Девяностые
Фантастика:
попаданцы
альтернативная история
6.80
рейтинг книги
Холодный ветер перемен

Часограмма

Щерба Наталья Васильевна
5. Часодеи
Детские:
детская фантастика
9.43
рейтинг книги
Часограмма

Академия проклятий. Книги 1 - 7

Звездная Елена
Академия Проклятий
Фантастика:
фэнтези
8.98
рейтинг книги
Академия проклятий. Книги 1 - 7

Эволюционер из трущоб. Том 3

Панарин Антон
3. Эволюционер из трущоб
Фантастика:
попаданцы
аниме
фэнтези
фантастика: прочее
6.00
рейтинг книги
Эволюционер из трущоб. Том 3

Жития Святых (все месяцы)

Ростовский Святитель Дмитрий
Религия и эзотерика:
религия
православие
христианство
5.00
рейтинг книги
Жития Святых (все месяцы)

В тени пророчества. Дилогия

Кусков Сергей Анатольевич
Путь Творца
Фантастика:
фэнтези
3.40
рейтинг книги
В тени пророчества. Дилогия

Ротмистр Гордеев

Дашко Дмитрий Николаевич
1. Ротмистр Гордеев
Фантастика:
фэнтези
попаданцы
альтернативная история
5.00
рейтинг книги
Ротмистр Гордеев

Хозяйка дома в «Гиблых Пределах»

Нова Юлия
Любовные романы:
любовно-фантастические романы
5.75
рейтинг книги
Хозяйка дома в «Гиблых Пределах»

Найдёныш. Книга 2

Гуминский Валерий Михайлович
Найденыш
Фантастика:
альтернативная история
4.25
рейтинг книги
Найдёныш. Книга 2

Герцогиня в ссылке

Нова Юлия
2. Магия стихий
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Герцогиня в ссылке