Добавить в цитаты Настройки чтения

Страница 302 из 502

<b>delegate bool SomeOp&lt;in Т&gt;(Т obj);</b>

<b>// Объявить делегат, ковариантный по отношению к обобщенному типу Т.</b>

<b>delegate Т AnotherOp&lt;out Т, V&gt;(V obj);</b>

<b>class Alpha {</b>

<b>  public int Val { get; set; }</b>

<b>  public Alpha(int v) { Val = v; }</b>

<b>}</b>

<b>class Beta : Alpha {</b>

<b>  public Beta(int v) : base(v) { }</b>

<b>}</b>

<b>class GenDelegateVarianceDemo {</b>

<b>  // Возвратить логическое значение true, если значение</b>

<b>  // переменной obj.Val окажется четным,</b>

<b>  static bool IsEven(Alpha obj) {</b>

<b>    if ((obj.Val % 2) == 0) return true;</b>

<b>    return false;</b>

<b>  }</b>

<b>  static Beta ChangeIt(Alpha obj) {</b>

<b>    return new Beta(obj.Val + 2);</b>

<b>  }</b>

<b>  static void Main() {</b>

<b>    Alpha objA = new Alpha(4);</b>

<b>    Beta objB = new Beta(9);</b>

<b>    // Продемонстрировать сначала контравариантность.</b>

<b>    // Объявить делегат SomeOp&lt;Alpha&gt; и задать для него метод IsEven.</b>

<b>    SomeOp&lt;Alpha&gt; checklt = IsEven;</b>

<b>    // Объявить делегат SomeOp&lt;Beta&gt;.</b>

<b>    SomeOp&lt;Beta&gt; checklt2;</b>

<b>    //А теперь- присвоить делегат SomeOp&lt;Alpha&gt; делегату SomeOp&lt;Beta&gt;.</b>

<b>    // *** Это допустимо только благодаря контравариантности. ***</b>

<b>    checklt2 = checklt;</b>

<b>    // Вызвать метод через делегат.</b>

<b>    Console.WriteLine(checklt2(objB));</b>

<b>    // Далее, продемонстрировать контравариантность.</b>

<b>    // Объявить сначала два делегата типа AnotherOp.</b>

<b>    // Здесь возвращаемым типом является класс Beta,</b>

<b>    //а параметром типа — класс Alpha.</b>

<b>    // Обратите внимание на то, что для делегата modifylt</b>

<b>    // задается метод Changelt.</b>

<b>    AnotherOp&lt;Beta, Alpha&gt; modifyIt = ChangeIt;</b>

<b>    // Здесь возвращаемым типом является класс Alpha,</b>

<b>    // а параметром типа — тот же класс Alpha.</b>

<b>    AnotherOp&lt;Alpha, Alpha&gt; modifyIt2;</b>

<b>    // А теперь присвоить делегат modifylt делегату modifyIt2.</b>

<b>    // *** Это допустимо только благодаря ковариантности. ***</b>

<b>    modifyIt2 = modifyIt;</b>

<b>    // Вызвать метод и вывести результаты на экран.</b>

<b>    objA = modifyIt2(objA);</b>

<b>    Console.WriteLine(objA.Val);</b>

<b>  }</b>

<b>}</b>

Выполнение этой программы приводит к следующему результату.

<b>False</b>

<b>6</b>

Каждая операция достаточно подробно поясняется в комментариях к данной программе. Следует особо подчеркнуть, для успешной компиляции программы в объявлении обоих типов делегатов SomeOp and AnotherOp должны быть непременно указаны ключевые слова in и out соответственно. Без этих модификаторов компиляция программы будет выполнена с ошибками из-за отсутствия неявных преобразований типов в означенных строках кода.

Создание экземпляров объектов обобщенных типов

Когда приходится иметь дело с обобщениями, то нередко возникает вопрос: не приведет ли применение обобщенного класса к неоправданному раздуванию кода? Ответ на этот вопрос прост: не приведет. Дело в том, что в C# обобщения реализованы весьма эффективным образом: новые объекты конструируемого типа создаются лишь по мере надобности. Этот процесс описывается ниже.

Когда обобщенный класс компилируется в псевдокод MSIL, он сохраняет все свои параметры типа в их обобщенной форме. А когда конкретный экземпляр класса потребуется во время выполнения программы, то JIT-компилятор сконструирует конкретный вариант этого класса в исполняемом коде, в котором параметры типа заменяются аргументами типа. В каждом экземпляре с теми же самыми аргументами типа будет использоваться один и тот же вариант данного класса в исполняемом коде.

Так, если имеется некоторый обобщенный класс Gen&lt;T&gt;, то во всех объектах типа Gen&lt;T&gt; будет использоваться один и тот же исполняемый код данного класса. Следовательно, раздувание кода исключается благодаря тому, что в программе создаются только те варианты класса, которые действительно требуются. Когда же возникает потребность сконструировать объект другого типа, то компилируется новый вариант класса в исполняемом коде.