Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C# - лекции IntUit (Биллиг В.А.).pdf
Скачиваний:
140
Добавлен:
13.02.2015
Размер:
4.13 Mб
Скачать

Универсальность - это механизм, воздействующий на все элементы языка. Поэтому он применим ко всем частным случаям классов C# .

Универсальные структуры

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

public struct Point<T>

{

T x, y;//координаты точки, тип которых задан параметром // другие свойства и методы структуры

}

Универсальные интерфейсы

Интерфейсы чаще всего следует делать универсальными, предоставляя большую гибкость для позднейших этапов создания системы. Возможно, вы заметили применение в наших примерах универсальных интерфейсов библиотеки FCL - IComparable<T> и других. Введение универсальности, в первую очередь, сказалось на библиотеке FCL - внутренних классов, определяющих поведение системы. В частности, для большинства интерфейсов появились универсальные двойники с параметрами. Если бы в наших примерах мы использовали не универсальный интерфейс, а обычный, то потеряли бы в эффективности, поскольку сравнение объектов потребовало бы создание временных объектов типа object, выполнения операций boxing и unboxing.

Универсальные делегаты

Делегаты также могут иметь родовые параметры. Чаще встречается ситуация, когда делегат объявляется в универсальном классе и использует в своем объявлении параметры универсального класса. Давайте рассмотрим ситуацию с делегатами более подробно. Вот объявление универсального класса, не очень удачно названного Delegate, в котором объявляется функциональный тип - delegate:

class Delegate<T>

{

public delegate T Del(T a, T b);

}

Как видите, тип аргументов и возвращаемого значения в сигнатуре функционального типа определяется классом Delegate.

Добавим в класс функцию высшего порядка FunAr, одним из аргументов которой будет функция типа Del, заданного делегатом. Эта функция будет применяться к элементам массива, передаваемого также функции FunAr. Приведу описание:

public T FunAr(T[] arr, T a0, Del f)

{

T temp = a0;

for(int i =0; i<arr.Length; i++)

{

temp = f(temp, arr[i]);

}

return (temp);

}

Эта универсальная функция с успехом может применяться для вычисления сумм, произведения, минимума и других подобных характеристик массива.

Рассмотрим теперь клиентский класс Testing, в котором определен набор функций:

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; }

Хотя все функции имеют разные типы, все они соответствуют определению класса Del - имеют два аргумента одного типа и возвращают результат того же типа. Посмотрим, как они применяются в тестирующем методе класса Testing:

public void TestFun()

{

int[] ar1 = { 3, 5, 7, 9 }; double[] ar2 = { 3.5, 5.7, 7.9 };

string[] ar3 = { "Мама ", "мыла ", "Машу ", "мылом." }; float[] ar4 = { 5f, 7f, 9f, 11f };

Delegate<int> d1 = new Delegate<int>(); Delegate<int>.Del del1;

del1= this.max2;

int max = d1.FunAr(ar1, ar1[0], del1); Console.WriteLine("max= {0}", max); Delegate<double> d2 = new Delegate<double>(); Delegate<double>.Del del2;

del2 = this.min2;

double min = d2.FunAr(ar2, ar2[0], del2); Console.WriteLine("min= {0}", min); Delegate<string> d3 = new Delegate<string>(); Delegate<string>.Del del3;

del3 = this.sum2;

string sum = d3.FunAr(ar3, "", del3); Console.WriteLine("concat= {0}", sum); Delegate<float> d4 = new Delegate<float>(); Delegate<float>.Del del4;

del4 = this.prod2;

float prod = d4.FunAr(ar4, 1f, del4); Console.WriteLine("prod= {0}", prod);

}

Обратите внимание на объявление экземпляра делегата:

Delegate<int>.Del del1;

В момент объявления задается фактический тип, и сигнатура экземпляра становится конкретизированной. Теперь экземпляр можно создать и связать с конкретной функцией. В C# 2.0 это делается проще и естественнее, чем ранее, - непосредственным присваиванием:

del1= this.max2;

При выполнении этого присваивания производятся довольно сложные действия - проверяется соответствие сигнатуры функции в правой части и экземпляра делегата, в

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]