355 500 произведений, 25 200 авторов.

Электронная библиотека книг » Герберт Шилдт » C# 4.0: полное руководство » Текст книги (страница 48)
C# 4.0: полное руководство
  • Текст добавлен: 6 апреля 2017, 04:00

Текст книги "C# 4.0: полное руководство"


Автор книги: Герберт Шилдт



сообщить о нарушении

Текущая страница: 48 (всего у книги 83 страниц)

Переопределение виртуальных методов в обобщенном классе

В обобщенном классе виртуальный метод может быть переопределен таким же образом, как и любой другой метод. В качестве примера рассмотрим следующую программу, в которой переопределяется виртуальный метод GetOb().

// Пример переопределения виртуального метода в обобщенном классе,

using System;

// Обобщенный базовый класс,

class Gen {

  protected T ob;

  public Gen(T о) {

    ob = о;

  }

  // Возвратить значение переменной ob.

  // Этот метод является виртуальным.

  public virtual T GetOb() {

    Console.Write(«Метод GetOb() из класса Gen» + " возвращает результат: ");

    return ob;

  }

}

// Класс, производный от класса Gen. В этом классе

// переопределяется метод GetOb().

class Gen2 : Gen {

  public Gen2(T o) : base(o) { }

  // Переопределить метод GetOb().

  public override T GetOb() {

    Console.Write(«Метод GetOb() из класса Gen2» + " возвращает результат: ");

    return ob;

  }

}

// Продемонстрировать переопределение метода в обобщенном классе,

class OverrideDemo {

  static void Main() {

    // Создать объект класса Gen с параметром типа int.

    Gen iOb = new Gen(88);

    // Здесь вызывается вариант метода GetOb() из класса Gen.

    Console.WriteLine(iOb.GetOb());

    //А теперь создать объект класса Gen2 и присвоить

    // ссылку на него переменной iOb типа Gen.

    iOb = new Gen2(99);

    // Здесь вызывается вариант метода GetOb() из класса Gen2.

    Console.WriteLine(iOb.GetOb());

  }

}

Ниже приведен результат выполнения этой программы.

Метод GetOb() из класса Gen возвращает результат: 88

Метод GetOb() из класса Gen2 возвращает результат: 99

Как следует из результата выполнения приведенной выше программы, переопределяемый вариант метода GetOb() вызывается для объекта типа Gen2, а его вариант из базового класса вызывается для объекта типа Gen.

Обратите внимание на следующую строку кода.

iOb = new Gen2(99);

Такое присваивание вполне допустимо, поскольку iOb является переменной типа Gen. Следовательно, она может ссылаться на любой объект типа Gen или же объект класса, производного от Gen, включая и Gen2. Разумеется, переменную iOb нельзя использовать, например, для ссылки на объект типа Gen2, поскольку это может привести к несоответствию типов.


Перегрузка методов с несколькими параметрами типа

Методы, параметры которых объявляются с помощью параметров типа, могут быть перегружены. Но правила их перегрузки упрощаются по сравнению с методами без параметров типа. Как правило, метод, в котором параметр типа служит для указания типа данных параметра этого метода, может быть перегружен при условии, что сигнатуры обоих его вариантов отличаются. Это означает, что оба варианта перегружаемого метода должны отличаться по типу или количеству их параметров. Но типовые различия должны определяться не по параметру обобщенного типа, а исходя из аргумента типа, подставляемого вместо параметра типа при конструировании объекта этого типа. Следовательно, метод с параметрами типа может быть перегружен таким образом, что он окажется пригодным не для всех возможных случаев, хотя и будет выглядеть верно.

В качестве примера рассмотрим следующий обобщенный класс.

// Пример неоднозначности, к которой может привести

// перегрузка методов с параметрами типа.

//

// Этот код не подлежит компиляции,

using System;

// Обобщенный класс, содержащий метод Set(), перегрузка

// которого может привести к неоднозначности,

class Gen {

  T ob1;

  V ob2;

  // ...

  // В некоторых случаях эти два метода не будут

  // отличаться своими параметрами типа,

  public void Set(T о) {

    ob1 = о;

  }

  public void Set(V o) {

    ob2 = o;

  }

}

class AmbiguityDemo {

  static void Main() {

    Gen ok = new Gen();

    Gen notOK = new Gen();

    ok.Set(10); // верно, поскольку аргументы типа отличаются

    notOK.Set(10); // неоднозначно, поскольку аргументы ничем не отличаются!

  }

}

Рассмотрим приведенный выше код более подробно. Прежде всего обратите внимание на то, что класс Gen объявляется с двумя параметрами типа: Т и V. В классе Gen метод Set() перегружается по параметрам типа Т и V, как показано ниже.

public void Set (T о) {

  ob1 = о;

}

public void Set(V o) {

  ob2 = o;

}

Такой подход кажется вполне обоснованным, поскольку типы Т и V ничем внешне не отличаются. Но подобная перегрузка таит в себе потенциальную неоднозначность.

При таком объявлении класса Gen не соблюдается никаких требований к различению типов Т и V. Например, нет ничего принципиально неправильного в том, что объект класса Gen будет сконструирован так, как показано ниже.

Gen notOK = new Gen();

В данном случае оба типа, Т и V, заменяются типом int. В итоге оба варианта метода Set() оказываются совершенно одинаковыми, что, разумеется, приводит к ошибке. Следовательно, при последующей попытке вызвать метод Set() для объекта notOK в методе Main() появится сообщение об ошибке вследствие неоднозначности во время компиляции.

Как правило, методы с параметрами типа перегружаются при условии, что объект конструируемого типа не приводит к конфликту. Следует, однако, иметь в виду, что ограничения на типы не учитываются при разрешении конфликтов, возникающих при перегрузке методов. Поэтому ограничения на типы нельзя использовать для исключения неоднозначности. Конструкторы, операторы и индексаторы с параметрами типа могут быть перегружены аналогично конструкторам по тем же самым правилам.


Ковариантность и контравариантность в параметрах обобщенного типа

В главе 15 ковариантность и контравариантность были рассмотрены в связи с необобщенными делегатами. Эта форма ковариантности и контравариантности по-прежнему поддерживается в С#, поскольку она очень полезна. Но в версии C# 4.0 возможности ковариантности и контравариантности были расширены до параметров обобщенного типа, применяемых в обобщенных интерфейсах и делегатах. Ковариантность и контравариантность применяется, главным образом, для рационального разрешения особых ситуаций, возникающих в связи с применением обобщенных интерфейсов и делегатов, определенных в среде .NET Framework. И поэтому некоторые интерфейсы и делегаты, определенные в библиотеке, были обновлены, чтобы использовать ковариантность и контравариантность параметров типа. Разумеется, преимуществами ковариантности и контравариантности можно также воспользоваться в интерфейсах и делегатах, создаваемых собственными силами. В этом разделе механизмы ковариантности и контравариантности параметров типа поясняются на конкретных примерах.


Применение ковариантности в обобщенном интерфейсе

Применительно к обобщенному интерфейсу ковариантность служит средством, разрешающим методу возвращать тип, производный от класса, указанного в параметре типа. В прошлом возвращаемый тип должен был в точности соответствовать параметру типа в силу строгой проверки обобщений на соответствие типов. Ковариантность смягчает это строгое правило таким образом, чтобы обеспечить типовую безопасность. Параметр ковариантного типа объявляется с помощью ключевого слова out, которое предваряет имя этого параметра.

Для того чтобы стали понятнее последствия применения ковариантности, обратимся к конкретному примеру. Ниже приведен очень простой интерфейс IMyCoVarGenIF, в котором применяется ковариантность.

//В этом обобщенном интерфейсе поддерживается ковариантность,

public interface IMyCoVarGenIF {

  Т GetObject();

}

Обратите особое внимание на то, как объявляется параметр обобщенного типа Т. Его имени предшествует ключевое слово out. В данном контексте ключевое слово out обозначает, что обобщенный тип Т является ковариантным. А раз он ковариантный, то метод GetObject() может возвращать ссылку на обобщенный тип Т или же ссылку на любой класс, производный от типа Т.

Несмотря на свою ковариантность по отношению к обобщенному типу Т, интерфейс IMyCoVarGenIF реализуется аналогично любому другому обобщенному интерфейсу. Ниже приведен пример реализации этого интерфейса в классе MyClass.

// Реализовать интерфейс IMyCoVarGenIF.

class MyClass : IMyCoVarGenIF {

  T obj;

  public MyClass(T v) {

    obj = v;

  }

  public T GetObject() {

    return obj;

  }

}

Обратите внимание на то, что ключевое слово out не указывается еще раз в выражении, объявляющем реализацию данного интерфейса в классе MyClass. Это не только не нужно, но и вредно, поскольку всякая попытка еще раз указать ключевое слово out будет расцениваться компилятором как ошибка.

А теперь рассмотрим следующую простую реализацию иерархии классов.

// Создать простую иерархию классов,

class Alpha {

  string name;

  public Alpha(string n) { name = n; }

  public string GetName() { return name; }

// ...

}

class Beta : Alpha {

  public Beta(string n) : base (n) { }

// ...

}

Как видите, класс Beta является производным от класса Alpha.

С учетом всего изложенного выше, следующая последовательность операций будет считаться вполне допустимой.

// Создать ссылку из интерфейса IMyCoVarGenIF на объект типа MyClass.

// Это вполне допустимо как при наличии ковариантности, так и без нее.

IMyCoVarGenIF AlphaRef =

         new MyClass(new Alpha(«Alpha #1»));

Console.WriteLine("Имя объекта, на который ссылается переменная AlphaRef: " +

        AlphaRef.GetObject().GetName());

//А теперь создать объект MyClass и присвоить его переменной AlphaRef.

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

AlphaRef = new MyClass(new Beta(«Beta #1»));

Console.WriteLine("Имя объекта, на который теперь ссылается " +

       "переменная AlphaRef: " + AlphaRef.GetObject().GetName());

Прежде всего, переменной AlphaRef типа IMyCoVarGenIF в этом фрагменте кода присваивается ссылка на объект типа MyClass. Это вполне допустимая операция, поскольку в классе MyClass реализуется интерфейс IMyCoVarGenIF, причем и в том, и в другом в качестве аргумента типа указывается Alpha. Далее имя объекта выводится на экран при вызове метода GetName() для объекта, возвращаемого методом GetObject(). И эта операция вполне допустима, поскольку Alpha – это и тип, возвращаемый методом GetName(), и обобщенный тип Т. После этого переменной AlphaRef присваивается ссылка на экземпляр объекта типа MyClass, что также допустимо, потому что класс Beta является производным от класса Alpha, а обобщенный тип Т – ковариантным в интерфейсе IMyCoVarGenIF. Если бы любое из этих условий не выполнялось, данная операция оказалась бы недопустимой.

Ради большей наглядности примера вся рассмотренная выше последовательность операций собрана ниже в единую программу.

// Продемонстрировать ковариантность в обобщенном интерфейсе,

using System;

// Этот обобщенный интерфейс поддерживает ковариантность.

public interface IMyCoVarGenIF {

  Т GetObject();

}

// Реализовать интерфейс IMyCoVarGenIF.

class MyClass : IMyCoVarGenIF {

  T obj;

  public MyClass(T v) { obj = v; }

  public T GetObject() { return obj; }

}

// Создать простую иерархию классов,

class Alpha {

  string name;

  public Alpha(string n) { name = n; }

  public string GetName() { return name; }

  // ...

}

class Beta : Alpha {

  public Beta(string n) : base(n) { }

  // ...

}

class VarianceDemo {

  static void Main() {

    // Создать ссылку из интерфейса IMyCoVarGenIF на объект типа MyClass.

    // Это вполне допустимо как при наличии ковариантности, так и без нее.

    IMyCoVarGenIF AlphaRef =

           new MyClass(new Alpha(«Alpha #1»));

    Console.WriteLine("Имя объекта, на который ссылается переменная " +

          "AlphaRef: " + AlphaRef.GetObject().GetName());

    //А теперь создать объект MyClass и присвоить его // переменной AlphaRef.

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

    AlphaRef = new MyClass(new Beta(«Beta #1»));

    Console.WriteLine("Имя объекта, на который теперь ссылается переменная "

         + "AlphaRef: " + AlphaRef.GetObject().GetName());

  }

}

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

Имя объекта, на который ссылается переменная AlphaRef: Alpha #1

Имя объекта, на который теперь ссылается переменная AlphaRef: Beta #1

Следует особо подчеркнуть, что переменной AlphaRef можно присвоить ссылку на объект типа MyClass благодаря только тому, что обобщенный тип Т указан как ковариантный в интерфейсе IMyCoVarGenIF. Для того чтобы убедиться в этом, удалите ключевое слово out из объявления параметра обобщенного типа Т в интерфейсе IMyCoVarGenIF и попытайтесь скомпилировать данную программу еще раз. Компиляция завершится неудачно, поскольку строгая проверка на соответствие типов не разрешит теперь подобное присваивание.

Один обобщенный интерфейс может вполне наследовать от другого. Иными словами, обобщенный интерфейс с параметром ковариантного типа можно расширить, как показано ниже.

public interface IMyCoVarGenIF2 : IMyCoVarGenIF {

// ...

}

Обратите внимание на то, что ключевое слово out указано только в объявлении расширенного интерфейса. Указывать его в объявлении базового интерфейса не только не нужно, но и не допустимо. И последнее замечание: обобщенный тип Т допускается не указывать как ковариантный в объявлении интерфейса IMyCoVarGenIF2. Но при этом исключается ковариантность, которую может обеспечить расширенный интерфейс IMyCoVarGetlF. Разумеется, возможность сделать интерфейс IMyCoVarGenIF2 инвариантным может потребоваться в некоторых случаях его применения.

На применение ковариантности накладываются некоторые ограничения. Ковариантность параметра типа может распространяться только на тип, возвращаемый методом. Следовательно, ключевое слово out нельзя применять в параметре типа, служащем для объявления параметра метода. Ковариантность оказывается пригодной только для ссылочных типов. Ковариантный тип нельзя использовать в качестве ограничения в интерфейсном методе. Так, следующий интерфейс считается недопустимым.

public interface IMyCoVarGenIF2 {

  void M() where V:T; // Ошибка, ковариантный тип T нельзя

  // использовать как ограничение

}


Применение контравариантности в обобщенном интерфейсе

Применительно к обобщенному интерфейсу контравариантность служит средством, разрешающим методу использовать аргумент, тип которого относится к базовому классу, указанному в соответствующем параметре типа. В прошлом тип аргумента метода должен был в точности соответствовать параметру типа в силу строгой проверки обобщений на соответствие типов. Контравариантность смягчает это строгое правило таким образом, чтобы обеспечить типовую безопасность. Параметр контравариантного типа объявляется с помощью ключевого слова in, которое предваряет имя этого параметра.

Для того чтобы стали понятнее последствия применения ковариантности, вновь обратимся к конкретному примеру. Ниже приведен обобщенный интерфейс IMyContraVarGenIF контравариантного типа. В нем указывается контравариантный параметр обобщенного типа Т, который используется в объявлении метода Show().

// Это обобщенный интерфейс, поддерживающий контравариантность.

public interface IMyContraVarGenIF {

  void Show(T obj);

}

Как видите, обобщенный тип Т указывается в данном интерфейсе как контравариантный с помощью ключевого слова in, предшествующего имени его параметра. Обратите также внимание на то, что Т является параметром типа для аргумента obj в методе Show().

Далее интерфейс IMyContraVarGenIF реализуется в классе MyClass, как показано ниже.

// Реализовать интерфейс IMyContraVarGenIF.

class MyClass IMyContraVarGenIF {

  public void Show(T x) {

    Console.WriteLine(x);

  }

}

В данном случае метод Show() просто выводит на экран строковое представление переменной х, получаемое в результате неявного обращения к методу ToString() из метода WriteLine().

После этого объявляется иерархия классов, как показано ниже.

// Создать простую иерархию классов,

class Alpha {

  public override string ToString() {

    return «Это объект класса Alpha.»;

  }

  // ...

}

class Beta : Alpha {

  public override string ToString() {

    return «Это объект класса Beta.»;

  }

  // ...

}

Ради большей наглядности классы Alpha и Beta несколько отличаются от аналогичных классов из предыдущего примера применения ковариантности. Обратите также внимание на то, что метод ToString() переопределяется таким образом, чтобы возвращать тип объекта.

С учетом всего изложенного выше, следующая последовательность операций будет считаться вполне допустимой.

// Создать ссылку из интерфейса IMyContraVarGenIF

//на объект типа MyClass.

// Это вполне допустимо как при наличии контравариантности, так и без нее.

 IMyContraVarGenIF AlphaRef = new MyClass();

// Создать ссылку из интерфейса IMyContraVarGenIF

// на объект типа MyClass.

//И это вполне допустимо как при наличии контравариантности, так и без нее.

IMyContraVarGenIF BetaRef = new MyClass();

// Создать ссылку из интерфейса IMyContraVarGenIF

// на объект типа MyClass.

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

IMyContraVarGenIF BetaRef2 = new MyClass();

// Этот вызов допустим как при наличии контравариантности, так и без нее.

BetaRef.Show(new Beta());

// Присвоить переменную AlphaRef переменной BetaRef.

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

BetaRef = AlphaRef;

BetaRef.Show(new Beta());

Прежде всего, обратите внимание на создание двух переменных ссылочного типа IMyContraVarGenIF, которым присваиваются ссылки на объекты класса MyClass, где параметры типа совпадают с аналогичными параметрами в интерфейсных ссылках. В первом случае используется параметр типа Alpha, а во втором – параметр типа Beta. Эти объявления не требуют контравариантности и допустимы в любом случае.

Далее создается переменная ссылочного типа IMyContraVarGenIF, но на этот раз ей присваивается ссылка на объект класса MyClass. Эта операция вполне допустима, поскольку обобщенный тип Т объявлен как контравариантный.

Как и следовало ожидать, следующая строка, в которой вызывается метод BetaRef.Show() с аргументом Beta, является вполне допустимой. Ведь Beta – это обобщенный тип Т в классе MyClass и в то же время аргумент в методе Show().

В следующей строке переменная AlphaRef присваивается переменной BetaRef. Эта операция вполне допустима лишь в силу контравариантности. В данном случае переменная относится к типу MyClass, а переменная AlphaRef – к типу MyClass. Но поскольку Alpha является базовым классом для класса Beta, то такое преобразование типов оказывается допустимым благодаря контравариантности. Для того чтобы убедиться в необходимости контравариантности в рассматриваемом здесь примере, попробуйте удалить ключевое слово in из объявления обобщенного типа Т в интерфейсе IMyContraVarGenIF, а затем попытайтесь скомпилировать приведенный выше код еще раз. В результате появятся ошибки компиляции.

Ради большей наглядности примера вся рассмотренная выше последовательность операций собрана ниже в единую программу.

// Продемонстрировать контравариантность в обобщенном интерфейсе,

using System;

// Это обобщенный интерфейс, поддерживающий контравариантность.

public interface IMyContraVarGenIF {

  void Show(T obj);

}

// Реализовать интерфейс IMyContraVarGenIF.

class MyClass : IMyContraVarGenIF {

  public void Show(T x) {

    Console.WriteLine(x);

  }

}

// Создать простую иерархию классов,

class Alpha {

  public override string ToString() {

    return «Это объект класса Alpha.»;

  }

  // ...

}

class Beta : Alpha {

  public override string ToString() {

    return «Это объект класса Beta.»;

  }

  // ...

}

class VarianceDemo {

  static void Main() {

    // Создать ссылку из интерфейса IMyContraVarGenIF

    //на объект типа MyClass.

    // Это вполне допустимо как при наличии контравариантности, так и без нее.

    IMyContraVarGenIF AlphaRef = new MyClass();

    // Создать ссылку из интерфейса IMyContraVarGenIF

    // на объект типа MyClass.

    //И это вполне допустимо как при наличии контравариантности,

    // так и без нее.

    IMyContraVarGenIF BetaRef = new MyClass();

    // Создать ссылку из интерфейса IMyContraVarGenIF

    //на объект типа MyClass.

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

    IMyContraVarGenIF BetaRef2 = new MyClass();

    // Этот вызов допустим как при наличии контравариантности, так и без нее.

    BetaRef.Show(new Beta());

    // Присвоить переменную AlphaRef переменной BetaRef.

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

    BetaRef = AlphaRef;

    BetaRef.Show(new Beta());

  }

}

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

Это объект класса Beta.

Это объект класса Beta.

Контравариантный интерфейс может быть расширен аналогично описанному выше расширению ковариантного интерфейса. Для достижения контравариантного характера расширенного интерфейса в его объявлении должен быть указан такой же параметр обобщенного типа, как и у базового интерфейса, но с ключевым словом in, как показано ниже.

public interface IMyContraVarGenIF2 : IMyContraVarGenIF {

  // ...

}

Следует иметь в виду, что указывать ключевое слово in в объявлении базового интерфейса не только не нужно, но и не допустимо. Более того, сам расширенный интерфейс IMyContraVarGenIF2 не обязательно должен быть контравариантным. Иными словами, обобщенный тип Т в интерфейсе IMyContraVarGenIF2 не требуется модифицировать ключевым словом in. Разумеется, все преимущества, которые сулит контравариантность в интерфейсе IMyContraVarGen, при этом будут утрачены в интерфейсе IMyContraVarGenIF2.

Контравариантность оказывается пригодной только для ссылочных типов, а параметр контравариантного типа можно применять только к аргументам методов. Следовательно, ключевое слово in нельзя указывать в параметре типа, используемом в качестве возвращаемого типа.


Вариантные делегаты

Как пояснялось в главе 15, ковариантность и контравариантность поддерживается в необобщенных делегатах в отношении типов, возвращаемых методами, и типов, указываемых при объявлении параметров. Начиная с версии C# 4.0, возможности ковариантности и контравариантности были распространены и на обобщенные делегаты. Подобные возможности действуют таким же образом, как было описано выше в отношении обобщенных интерфейсов.

Ниже приведен пример контравариантного делегата.

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

delegate bool SomeOp(Т obj);

Этому делегату можно присвоить метод с параметром обобщенного типа Т или же класс, производный от типа Т.

А вот пример ковариантного делегата.

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

delegate Т AnotherOp(V obj);

Этому делегату можно присвоить метод, возвращающий обобщенный тип Т, или же класс, производный от типа Т. В данном случае V оказывается просто параметром инвариантного типа.

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

// Продемонстрировать конвариантность и контравариантность

// в обобщенных делегатах.

using System;

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

delegate bool SomeOp(Т obj);

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

delegate Т AnotherOp(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 и задать для него метод IsEven.

    SomeOp checklt = IsEven;

    // Объявить делегат SomeOp.

    SomeOp checklt2;

    //А теперь– присвоить делегат SomeOp делегату SomeOp.

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

    checklt2 = checklt;

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

    Console.WriteLine(checklt2(objB));

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

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

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

    //а параметром типа – класс Alpha.

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

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

    AnotherOp modifyIt = ChangeIt;

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

    // а параметром типа – тот же класс Alpha.

    AnotherOp modifyIt2;

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

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

    modifyIt2 = modifyIt;

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

    objA = modifyIt2(objA);

    Console.WriteLine(objA.Val);

  }

}

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

False

6

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


    Ваша оценка произведения:

Популярные книги за неделю