Полное руководство. С# 4.0 - Шилдт Герберт
Шрифт:
Интервал:
Закладка:
А вот пример ковариантного делегата.// Объявить делегат, ковариантный по отношению к обобщенному типу Т.delegate Т AnotherOp<out Т, V>(V obj);
Этому делегату можно присвоить метод, возвращающий обобщенный тип Т, илиже класс, производный от типа Т. В данном случае V оказывается просто параметроминвариантного типа.
В следующем примере программы демонстрируется применение обоих разновидностей вариантных делегатов на практике.// Продемонстрировать конвариантность и контравариантность// в обобщенных делегатах.using System;// Объявить делегат, контравариантный по отношению к обобщенному типу Т.delegate bool SomeOp<in Т>(Т obj);// Объявить делегат, ковариантный по отношению к обобщенному типу Т.delegate Т AnotherOp<out Т, V>(V obj);class Alpha { public int Val { get; set; } public Alpha(int v) { Val = v; }}class Beta : Alpha { public Beta (int v) : base(v) { }}class GenDelegateVarianceDemo { // Возвратить логическое значение true, если значение // переменной obj.Val окажется четным. static bool IsEven(Alpha obj) { if((obj.Val % 2) == 0) return true; return false; } static Beta ChangeIt(Alpha obj) { return new Beta(obj.Val +2); } static void Main() { Alpha objA = new Alpha(4); Beta objB = new Beta(9); // Продемонстрировать сначала контравариантность. // Объявить делегат SomeOp<Alpha> и задать для него метод IsEven. SomeOp<Alpha> checkIt = IsEven; // Объявить делегат SomeOp<Beta>. SomeOp<Beta> checkIt2; // А теперь- присвоить делегат SomeOp<Alpha> делегату SomeOp<Beta>. // *** Это допустимо только благодаря контравариантности. *** checklt2 = checkIt; // Вызвать метод через делегат. Console.WriteLine(checkIt2(objВ)); // Далее, продемонстрировать контравариантность. // Объявить сначала два делегата типа AnotherOp. // Здесь возвращаемым типом является класс Beta, // а параметром типа - класс Alpha. // Обратите внимание на то, что для делегата modifyIt // задается метод ChangeIt. AnotherOp<Beta, Alpha> modifyIt = ChangeIt; // Здесь возвращаемым типом является класс Alpha, // а параметром типа — тот же класс Alpha. AnotherOp<Alpha, Alpha> modifyIt2; // А теперь присвоить делегат modifyIt делегату modifyIt2. // *** Это допустимо только благодаря ковариантности. *** modifyIt2 = modifyIt; // Вызвать метод и вывести результаты на экран. objA = modifyIt2(objА); Console.WriteLine(objA.Val); }}
Выполнение этой программы приводит к следующему результату.False6
Каждая операция достаточно подробно поясняется в комментариях к данной программе. Следует особо подчеркнуть, для успешной компиляции программы в объявлении обоих типов делегатов SomeOp and AnotherOp должны быть непременно указаны ключевые слова in и out соответственно. Без этих модификаторов компиляцияпрограммы будет выполнена с ошибками из-за отсутствия неявных преобразованийтипов в означенных строках кода.Создание экземпляров объектов обобщенных типов
Когда приходится иметь дело с обобщениями, то нередко возникает вопрос: неприведет ли применение обобщенного класса к неоправданному раздуванию кода?Ответ на этот вопрос прост: не приведет. Дело в том, что в C# обобщения реализованывесьма эффективным образом: новые объекты конструируемого типа создаются лишьпо мере надобности. Этот процесс описывается ниже.
Когда обобщенный класс компилируется в псевдокод MSIL, он сохраняет все своипараметры типа в их обобщенной форме. А когда конкретный экземпляр класса потребуется во время выполнения программы, то JIT-компилятор сконструирует конкретный вариант этого класса в исполняемом коде, в котором параметры типа заменяются аргументами типа. В каждом экземпляре с теми же самыми аргументами типабудет использоваться один и тот же вариант данного класса в исполняемом коде.
Так, если имеется некоторый обобщенный класс Gen, то во всех объектах типаGen будет использоваться один и тот же исполняемый код данного класса. Следовательно, раздувание кода исключается благодаря тому, что в программе создаются только те варианты класса, которые действительно требуются. Когда же возникаетпотребность сконструировать объект другого типа, то компилируется новый варианткласса в исполняемом коде.
Как правило, новый исполняемый вариант обобщенного класса создается для каждого объекта конструируемого типа, в котором аргумент имеет тип значения, напримерint или double. Следовательно, в каждом объекте типа Gen будет использоваться один исполняемый вариант класса Gen, а в каждом объекте типа Gen —другой вариант класса Gen, причем каждый вариант приспосабливается к конкретному типу значения. Но во всех случаях, когда аргумент оказывается ссылочного типа,используется только один вариант обобщенного класса, поскольку все ссылки имеютодинаковую длину (в байтах). Такая оптимизация также исключает раздувание кода.Некоторые ограничения, присущие обобщениям
Ниже перечислен ряд ограничений, которые следует иметь в виду при использовании обобщений.
Свойства, операторы, индексаторы и события не могут быть обобщенными. Но эти элементы могут использоваться в обобщенном классе, причем с параметрами обобщенного типа этого класса.
К обобщенному методу нельзя применять модификатор extern.
Типы указателей нельзя использовать в аргументах типа.
Если обобщенный класс содержит поле типа static, то в объекте каждого конструируемого типа должна быть своя копия этого поля. Эго означает, что во всех экземплярах объектов одного конструируемого типа совместно используется одно и то же поле типа static. Но в экземплярах объектов другого конструируемого типа совместно используется другая копия этого поля. Следовательно, поле типа static не может совместно использоваться объектами всех конструируемых типов.Заключительные соображения относительно обобщений
Обобщения являются весьма эффективным дополнением С#, поскольку они упрощают создание типизированного, повторно используемого кода. Несмотря на несколько усложненный, на первый взгляд, синтаксис обобщений, их применение быстровходит в привычку. Аналогично, умение применять ограничения к месту требует некоторой практики и со временем не вызывает особых затруднений. Обобщения теперьстали неотъемлемой частью программирования на С#. Поэтому освоение этого важного языкового средства стоит затраченных усилий.
ГЛАВА 19. LINQ
Без сомнения, LINQ относится к одним из самых интересных средств языка С#. Эти средства были внедреныв версии C# 3.0 и явились едва ли не самым главнымего дополнением, которое состояло не только во внесениисовершенно нового элемента в синтаксис С#, добавлениинескольких ключевых слов и предоставлении больших возможностей, но и в значительном расширении рамок данного языка программирования и круга задач, которые онпозволяет решать. Проще говоря, внедрение LINQ сталоповоротным моментом в истории развития С#.
Аббревиатура LINQ означает Language-Integrated Query,т.е. язык интегрированных запросов. Это понятие охватываетряд средств, позволяющих извлекать информацию из источника данных. Как вам должно быть известно, извлечениеданных составляет важную часть многих программ. Например, программа может получать информацию из списказаказчиков, искать информацию в каталоге продукции илиполучать доступ к учетному документу, заведенному на работника. Как правило, такая информация хранится в базеданных, существующей отдельно от приложения. Так, каталог продукции может храниться в реляционной базе данных. В прошлом для взаимодействия с такой базой данныхприходилось формировать запросы на языке структурированных запросов (SQL). А для доступа к другим источникамданных, например в формате XML, требовался отдельныйподход. Следовательно, до версии 3.0 поддержка подобныхзапросов в C# отсутствовала. Но это положение изменилосьпосле внедрения LINQ.
LINQ дополняет C# средствами, позволяющими формировать запросы для любого LINQ-совместимого источникаданных. При этом синтаксис, используемый для формирования запросов, остается неизменным, независимо от типа источника данных. Это, в частности, означает, что синтаксис, требующийся для формирования запроса к реляционной базе данных, практически ничем не отличается от синтаксиса запроса данных, хранящихся в массиве.Для этой цели теперь не нужно прибегать к средствам SQL или другого внешнего поотношению к C# механизма извлечения данных из источника. Возможности формировать запросы отныне полностью интегрированы в язык С#.