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

на главную - закладки

Жанры

Полное руководство. С# 4.0
Шрифт:

В приведенном ниже примере программы демонстрируется применение делегата SomeOp с одним параметром типа Т. Этот делегат возвращает значение типа Т и при нимает аргумент типа Т. // Простой пример обобщенного делегата. using System; // Объявить обобщенный делегат. delegate Т SomeOp<T>(T v); class GenDelegateDemo { // Возвратить результат суммирования аргумента. static int Sum(int v) { int result = 0; for(int i=v; i>0; i--) result += i; return result; } // Возвратить строку, содержащую обратное значение аргумента. static string Reflect(string str) { string result = ""; foreach(char ch in str) result = ch + result; return result; } static void Main { // Сконструировать делегат типа int. SomeOp<int> intDel = Sum; Console.WriteLine(intDel(3)); // Сконструировать делегат типа string. SomeOp<string> strDel = Reflect; Console.WriteLine(strDel("Привет")); } }

Эта

программа дает следующий результат. 6 тевирП

Рассмотрим эту программу более подробно. Прежде всего обратите внимание на следующее объявление делегата SomeOp. delegate Т SomeOp<T>(Т v);

Как видите, тип Т может служить в качестве возвращаемого типа, несмотря на то, что параметр типа Т указывается после имени делегата SomeOp.

Далее в классе GenDelegateDemo объявляются методы Sum и Reflect, как по казано ниже. static int Sum(int v) { static string Reflect(string str) {

Метод Sum возвращает результат суммирования целого значения, передаваемого в качестве аргумента, а метод Reflect — символьную строку, которая получается об ращенной по отношению к строке, передаваемой в качестве аргумента.

В методе Main создается экземпляр intDel делегата, которому присваивается ссылка на метод Sum. SomeOp<int> intDel = Sum;

Метод Sum принимает аргумент типа int и возвращает значение типа int, поэ тому он совместим с целочисленным экземпляром делегата SomeOp.

Аналогичным образом создается экземпляр strDel делегата, которому присваива ется ссылка на метод Reflect. SomeOp<string> strDel = Reflect;

Метод Reflect принимает аргумент типа string и возвращает результат типа string, поэтому он совместим со строковым экземпляром делегата SomeOp. В силу присущей обобщениям типовой безопасности обобщенным делегатам нель зя присваивать несовместимые методы. Так, следующая строка кода оказалась бы оши бочной в рассматриваемой здесь программе. SomeOp<int> intDel = Reflect; // Ошибка!

Ведь метод Reflect принимает аргумент типа string и возвращает результат типа string, а следовательно, он несовместим с целочисленным экземпляром деле гата SomeOp. Обобщенные интерфейсы

Помимо обобщенных классов и методов, в C# допускаются обобщенные интерфей сы. Такие интерфейсы указываются аналогично обобщенным классам. Ниже приведен измененный вариант примера из главы 12, демонстрирующего интерфейс ISeries. (Напомним, что ISeries является интерфейсом для класса, генерирующего последо вательный ряд числовых значений.) Тип данных, которым оперирует этот интерфейс, теперь определяется параметром типа. // Продемонстрировать применение обобщенного интерфейса. using System; public interface ISeries<T> { T GetNext;// возвратить следующее по порядку число void Reset; // генерировать ряд последовательных чисел с самого начала void SetStart(Т v); // задать начальное значение } // Реализовать интерфейс ISeries. class ByTwos<T> : ISeries<T> { T start; T val; // Этот делегат определяет форму метода, вызываемого для генерирования // очередного элемента в ряду последовательных значений. public delegate Т IncByTwo(T v); // Этой ссылке на делегат будет присвоен метод, // передаваемый конструктору класса ByTwos. IncByTwo incr; public ByTwos(IncByTwo incrMeth) { start = default(T); val = default(T); incr = incrMeth; } public T GetNext { val = incr(val); return val; } public void Reset { val = start; } public void SetStart(T v) { start = v; val = start; } } class ThreeD { public int x, y, z; public ThreeD(int a, int b, int c) { x = a; У = b; z = c; } } class GenlntfDemo { // Определить метод увеличения на два каждого // последующего значения типа int. static int IntPlusTwo (int v) { return v + 2; } // Определить метод увеличения на два каждого // последующего значения типа double. static double DoublePlusTwo (double v) { return v + 2.0; } // Определить метод увеличения на два каждого // последующего значения координат объекта типа ThreeD. static ThreeD ThreeDPlusTwo(ThreeD v) { if(v==null) return new ThreeD(0, 0, 0); else return new ThreeD(v.x + 2, v.y + 2, v.z + 2); } static void Main { // Продемонстрировать генерирование // последовательного ряда значений типа int. ByTwos<int> intBT = new ByTwos<int>(IntPlusTwo); for(int i=0; i < 5; i++) Console.Write(intBT.GetNext + " "); Console.WriteLine; //

Продемонстрировать генерирование // последовательного ряда значений типа double. ByTwos<double> dblBT = new ByTwos<double>(DoublePlusTwo); dblBT.SetStart(11.4); for (int i=0; i < 5; i++) Console.Write(dblBT.GetNext + " "); Console.WriteLine; // Продемонстрировать генерирование последовательного ряда // значений координат объекта типа ThreeD. ByTwos<ThreeD> ThrDBT = new ByTwos<ThreeD>(ThreeDPlusTwo); ThreeD coord; for(int i=0; i < 5; i++) { coord = ThrDBT.GetNext; Console.Write(coord.x + "," + coord.у + "," + coord.z + " "); } Console.WriteLine; } }

Этот код выдает следующий результат. 2 4 6 8 10 13.4 15.4 17.4 19.4 21.4 0,0,0 2,2,2 4,4,4 6,6,6 8,8,8

В данном примере кода имеется ряд любопытных моментов. Прежде всего обрати те внимание на объявление интерфейса ISeries в следующей строке кода. public interface ISeries<T> {

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

А теперь обратите внимание на следующее объявление класса ByTwos, реализую щего интерфейс Iseries. class ByTwos<T> : ISeries<T> {

Параметр типа Т указывается не только при объявлении класса ByTwos, но и при объявлении интерфейса ISeries. И это очень важно. Ведь класс, реализующий обоб щенный вариант интерфейса, сам должен быть обобщенным. Так, приведенное ниже объявление недопустимо, поскольку параметр типа Т не определен. class ByTwos : ISeries<T> { // Неверно!

Аргумент типа, требующийся для интерфейса ISeries, должен быть передан клас су ByTwos. В противном случае интерфейс никак не сможет получить аргумент типа. Далее переменные, хранящие текущее значение в последовательном ряду (val) и его начальное значение (start), объявляются как объекты обобщенного типа Т. По сле этого объявляется делегат IncByTwo. Этот делегат определяет форму метода, ис пользуемого для увеличения на два значения, хранящегося в объекте типа Т. Для того чтобы в классе ByTwos могли обрабатываться данные любого типа, необходимо каким- то образом определить порядок увеличения на два значения каждого типа данных. Для этого конструктору класса ByTwos передается ссылка на метод, выполняющий увеличение на два. Эта ссылка хранится в переменной экземпляра делегата incr. Ког да требуется сгенерировать следующий элемент в последовательном ряду, этот метод вызывается с помощью делегата incr.

А теперь обратите внимание на класс ThreeD. В этом классе инкапсулируются ко ординаты трехмерного пространства (X,Z,Y). Его назначение — продемонстрировать обработку данных типа класса в классе ByTwos.

Далее в классе GenIntfDemo объявляются три метода увеличения на два для объек тов типа int, double и ThreeD. Все эти методы передаются конструктору класса ByTwos при создании объектов соответствующих типов. Обратите особое внимание на приведенный ниже метод ThreeDPlusTwo. // Определить метод увеличения на два каждого // последующего значения координат объекта типа ThreeD. static ThreeD ThreeDPlusTwo(ThreeD v) { if(v==null) return new ThreeD(0, 0, 0); else return new ThreeD(v.x + 2, v.y + 2, v.z + 2); }

В этом методе сначала проверяется, содержит ли переменная экземпляра v пустое значение (null). Если она содержит это значение, то метод возвращает новый объект типа ThreeD со всеми обнуленными полями координат. Ведь дело в том, что перемен ной v по умолчанию присваивается значение типа default(Т) в конструкторе класса ByTwos. Это значение оказывается по умолчанию нулевым для типов значений и пу стым для типов ссылок на объекты. Поэтому если предварительно не был вызван ме тод SetStart, то перед первым увеличением на два переменная v будет содержать пустое значение вместо ссылки на объект. Это означает, что для первого увеличения на два требуется новый объект.

На параметр типа в обобщенном интерфейсе могут накладываться ограничения таким же образом, как и в обобщенном классе. В качестве примера ниже приведен вариант объявления интерфейса ISeries с ограничением на использование только ссылочных типов. public interface ISeries<T> where T : class {

Если реализуется именно такой вариант интерфейса ISeries, в реализующем его классе следует указать то же самое ограничение на параметр типа Т, как показано ниже. class ByTwos<T> : ISeries<T> where T : class {

Поделиться:
Популярные книги

Лишняя дочь

Nata Zzika
Любовные романы:
любовно-фантастические романы
8.22
рейтинг книги
Лишняя дочь

Маверик

Астахов Евгений Евгеньевич
4. Сопряжение
Фантастика:
боевая фантастика
постапокалипсис
рпг
5.00
рейтинг книги
Маверик

Мужчина моей судьбы

Ардова Алиса
2. Мужчина не моей мечты
Любовные романы:
любовно-фантастические романы
8.03
рейтинг книги
Мужчина моей судьбы

Развод, который ты запомнишь

Рид Тала
1. Развод
Любовные романы:
остросюжетные любовные романы
короткие любовные романы
5.00
рейтинг книги
Развод, который ты запомнишь

Кодекс Охотника. Книга XIV

Винокуров Юрий
14. Кодекс Охотника
Фантастика:
боевая фантастика
попаданцы
аниме
5.00
рейтинг книги
Кодекс Охотника. Книга XIV

Столкновение

Хабра Бал
1. Вне льда
Любовные романы:
современные любовные романы
5.00
рейтинг книги
Столкновение

Черный маг императора

Герда Александр
1. Черный маг императора
Фантастика:
юмористическая фантастика
попаданцы
аниме
5.00
рейтинг книги
Черный маг императора

Кодекс Крови. Книга V

Борзых М.
5. РОС: Кодекс Крови
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Кодекс Крови. Книга V

Любимая учительница

Зайцева Мария
1. совершенная любовь
Любовные романы:
современные любовные романы
эро литература
8.73
рейтинг книги
Любимая учительница

Адвокат империи

Карелин Сергей Витальевич
1. Адвокат империи
Фантастика:
городское фэнтези
попаданцы
фэнтези
5.75
рейтинг книги
Адвокат империи

Боярышня Дуняша 2

Меллер Юлия Викторовна
2. Боярышня
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Боярышня Дуняша 2

Убивать чтобы жить 9

Бор Жорж
9. УЧЖ
Фантастика:
героическая фантастика
боевая фантастика
рпг
5.00
рейтинг книги
Убивать чтобы жить 9

Новый Рал 7

Северный Лис
7. Рал!
Фантастика:
попаданцы
5.00
рейтинг книги
Новый Рал 7

Боги, пиво и дурак. Том 3

Горина Юлия Николаевна
3. Боги, пиво и дурак
Фантастика:
фэнтези
попаданцы
5.00
рейтинг книги
Боги, пиво и дурак. Том 3