Интерфейсы чаще всего следует делать универсальными, предоставляя большую гибкость для позднейших этапов создания системы. Возможно, вы заметили применение в наших примерах универсальных интерфейсов библиотеки FCL —
Универсальные делегаты
Делегаты также могут иметь родовые параметры. Чаще встречается ситуация, когда делегат объявляется в универсальном классе и использует в своем объявлении параметры универсального класса. Давайте рассмотрим ситуацию с делегатами более подробно. Вот объявление универсального класса, не очень удачно названного
class Delegate
{
public delegate T Del(T a, T b);
}
Как видите, тип аргументов и возвращаемого значения в сигнатуре функционального типа определяется классом
Добавим в класс функцию высшего порядка
public T FunAr(T[] arr, T a0, Del f)
{
T temp = a 0;
for (int i =0; i {
temp = f(temp, arr[i]);
}
return (temp);
}
Эта универсальная функция с успехом может применяться для вычисления сумм, произведения, минимума и других подобных характеристик массива.
Рассмотрим теперь клиентский класс
public int max2(int a, int b)
{ return (a > b)? a: b; }
public double min2(double a, double b)
{ return (a < b)? a: b; }
public string sum2(string a, string b)
{ return a + b; }
public float prod2(float a, float b)
{ return a * b; }
Хотя все функции имеют разные типы, все они соответствуют определению класса
public void TestFun()
int[] ar1 = { 3, 5, 7, 9 }; doublet] ar2 = { 3.5, 5.7, 7.9 };
string[] агЗ = { "Мама", "мыла", "Машу", "мылом." };
float[] ar4 = { 5f, 7f, 9f, 11f };
Delegate
Delegate
del1= this.max2;
int max = d1.FunAr(ar1, ar1[0], del1);
Console.WriteLine("max= {0}", max);
Delegate
Delegate
del2 = this.min2;
double min = d2.FunAr(ar2, ar2[0], del2);
Console.WriteLine("min= {0}", min);
Delegate
Delegate
del3 = this.sum2;
string sum = d3.FunAr(ar3, del3);
Console.WriteLine("concat= {0}", sum);
Delegate
Delegate
del4 = this.prod2;
float prod = d4.FunAr(ar4, If, del4);
Console.WriteLine("prod= {0}", prod);
}
Обратите внимание на объявление экземпляра делегата:
Delegate
В момент объявления задается фактический тип, и сигнатура экземпляра становится конкретизированной. Теперь экземпляр можно создать и связать с конкретной функцией. В C# 2.0 это делается проще и естественнее, чем ранее, — непосредственным присваиванием:
del1= this.max2;
При выполнении этого присваивания производятся довольно сложные действия — проверяется соответствие сигнатуры функции в правой части и экземпляра делегата, в случае успеха создается новый экземпляр делегата, который и связывается с функцией.
Покажем, что и сам функциональный тип-делегат можно объявлять с родовыми параметрами. Вот пример такого объявления:
public delegate T FunTwoArg