Чтение онлайн

ЖАНРЫ

Интернет-журнал "Домашняя лаборатория", 2007 №9
Шрифт:

Console.WriteLine("sum= {0}", list1.Sum);

SumList<string, double> list2 =

new SumList<string, double> (new DoubleCalc);

list2.add("Петр", 33.33); list2.add("Павел", 44.44);

Console.WriteLine("sum= {0}", list2.Sum);

SumList<string, string> list3 =

new SumList<string, string> (new StringCalc);

list3.add("Мама", " Мама мыла "); list3.add("Маша",

"Машу мылом!");

Console.WriteLine("sum= {0}", list3.Sum );

}

Обратите внимание на создание списков:

SumList<string, int> list1 =

new SumList<string, int>(new IntCalc);

SumList<string, double> list2 =

new SumList<string, double>(new DoubleCalc);

SumList<string, string> list3 =

new SumList<string, string>(new StringCalc);

Как

видите, конструктору объекта передается калькулятор, согласованный с типами данных, которые хранятся в списке. Результаты вычислений, полученных при работе с этими списками, приведены на рис. 22.6.

Рис. 22.6. Списки с суммированием

Родовое порождение класса. Предложение using

До сих пор рассматривалась ситуация родового порождения экземпляров универсального класса. Фактические типы задавались в момент создания экземпляра. Это наглядно показывает преимущества применяемой технологии, поскольку очевидно, что не создается дублирующий код для каждого класса, порожденного универсальным классом. И все-таки остается естественный вопрос: можно ли породить класс из универсального класса путем подстановки фактических параметров, а потом спокойно использовать этот класс обычным образом? Такая вещь возможна. Это можно сделать не совсем обычным путем — не в программном коде, а в предложении using, назначение которого и состоит в выполнении подобных подстановок.

Давайте вернемся к универсальному классу OneLinkstack<T>, введенному в начале этой лекции, и породим на его основе вполне конкретный класс IntStack, заменив формальный параметр T фактическим — int. Для этого достаточно задать следующее предложение using;

using IntStack = Generic.OneLinkStack<int>;

Вот тест, в котором создаются несколько объектов этого класса:

public void TestlntStack

{

IntStack stack1 = new IntStack ;

IntStack stack2 = new IntStack ;

IntStack stack3 = new IntStack ;

stack2.put (11); stackl.put (22);

int x1 = stack1.item, x2 = stack1.item;

if ((x1 == x2) && (xl == 22)) Console.WriteLine("OK!");

stack1.remove; x2 = stack1.item;

if ((x1!= x2) && (x2 == 11)) Console.WriteLine("OK!");

stack1.remove; x2 = (stack1.empty)? 77:

stack1.item;

if ((x1!= x2) && (x2 == 77)) Console.WriteLine("OK!");

stack2.put (55); stack2.put (66);

stack2.remove; int s = stack2.item;

if (!stack2.empty) Console.WriteLine(s);

stack3.put (33 3); stack3.put((int)Math.Sqrt(Math.PI));

int res = stack3.item;

stack3.remove; res += stack3.item;

Console.WriteLine("res= {0}", res);

}

Все работает заданным образом, можете поверить.

Универсальность

и специальные случаи классов

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

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

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

public struct Point<T>

{

Т х, у;//координаты точки, тип которых задан параметром

// другие свойства и методы структуры

}

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

Интерфейсы чаще всего следует делать универсальными, предоставляя большую гибкость для позднейших этапов создания системы. Возможно, вы заметили применение в наших примерах универсальных интерфейсов библиотеки FCL — IСоmраrаЫе<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 = a 0;

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)

Поделиться с друзьями: