C# 4.0 полное руководство - 2011
Шрифт:
Ковариантность и контравариантность
Делегаты становятся еще более гибкими средствами программирования благодаря двум свойствам: ковариантности и контравариантности. Как правило, метод, передаваемый делегату,-должен иметь такой же возвращаемый тип и сигнатуру, как и делегат. Но в отношении производных типов это правило оказывается не таким строгим благодаря ковариантности и контравариантности. В частности, ковариантность позволяет присвоить делегату метод, возвращаемым типом которого служит класс,
Ниже приведен пример, демонстрирующий ковариантность и контравариантность.
// Продемонстрировать ковариантность и контравариантность.
using System;
class X {
public int Val;
}
// Класс Y, производный от класса X. class Y : X { }
// Этот делегат возвращает объект класса X и // принимает объект класса Y в качестве аргумента, delegate X ChangeIt(Y obj);
class CoContraVariance {
// Этот метод возвращает объект класса X и // имеет объект класса X в качестве параметра, static X IncrA(X obj) {
X temp = new X ; temp.Val = obj.Val + 1; return •temp;
}
// Этот метод возвращает объект класса Y и // имеет объект класса Y в качестве параметра, static Y IncrB(Y obj) {
Y temp = new Y; temp.Val = obj.Val + 1; return temp;
}
static void Main {
Y Yob = new Y;
// В данном случае параметром метода IncrA является объект класса X,
// а параметром делегата Changelt — объект класса Y. Но благодаря // контравариантности следующая строка кода вполне допустима.
Changelt change = IncrA;
X Xob = change(Yob);
Console.WriteLine("Xob: " + Xob.Val);
// В этом случае возвращаемым типом метода IncrB служит объект класса Y, // а возвращаемым типом делегата Changelt — объект класса X. Но благодаря // ковариантности следующая строка кода оказывается вполне допустимой, change = IncrB;
Yob = (Y) change(Yob);
Console.WriteLine("Yob: " + Yob.Val);
}
}
Вот к какому результату приводит выполнение этого кода.
Xob: 1 Yob: 1
В данном примере класс Y является производным от класса X. А делегат Changelt объявляется следующим образом.
delegate X Changelt(Y obj);
Делегат возвращает объект класса X и принимает в качестве параметра объект класса Y. А методы IncrA и IncrB объявляются следующим образом.
static X IncrA(X obj) static Y IncrB(Y obj)
Метод IncrA
Таким образом, в строке
Changelt change = IncrA;
метод IncrA может быть передан делегату благодаря контравариантности, так как объект класса X служит в качестве параметра метода IncrA , а объект класса Y — в качестве параметра делегата Changelt. Но метод и делегат оказываются совместимыми в силу контравариантности, поскольку типом параметра метода, передаваемого делегату, служит класс, являющийся базовым для класса, указываемого в качестве типа параметра делегата.
Приведенная ниже строка кода также является вполне допустимой, но на этот раз благодаря ковариантности.
change = IncrB;
В данном случае возвращаемым типом для метода IncrB служит класс Y, а для делегата — класс X. Но поскольку возвращаемый тип метода является производным классом от возвращаемого типа делегата, то оба оказываются совместимыми в силу ковариантности.
Класс System. Delegate
Все делегаты и классы оказываются производными неявным образом от класса System. Delegate. Как правило, членами этого класса не пользуются непосредственно, и это Не делается явным образом в данной книге. Но члены класса System. Delegate могут оказаться полезными в ряде особых случаев.
Назначение делегатов
В предыдущих примерах был наглядно продемонстрирован внутренний механизм действия делегатов, но эти примеры не показывают их истинное назначение. Как правило, делегаты применяются по двум причинам. Во-первых, как упоминалось ранее в этой главе, делегаты поддерживают события. И во-вторых, делегаты позволяют вызывать методы во время выполнения программы, не зная о них ничего определенного в ходе компиляции. Это очень удобно для создания базовой конструкции, допускающей подключение отдельных программных компонентов. Рассмотрим в качестве примера графическую программу, аналогичную стандартной сервисной программе Windows Paint. С помощью делегата можно предоставить пользователю возможность подключать специальные цветные фильтры или анализаторы изображений. Кроме того, пользователь может составлять из этих фильтров или анализаторов целые последовательности. Подобные возможности программы нетрудно обеспечить, используя делегаты.