Студопедия.Орг Главная | Случайная страница | Контакты | Мы поможем в написании вашей работы!  
 

Прочие примеры универсальных шаблонов



Структура System.ArraySegment<T> является «обёрткой» над массивом, определяющей интервал элементов массива. Для одного массива можно создать несколько объектов ArraySegment<T>, которые могут задавать даже перекрывающиеся интервалы. Работать с «обёрнутым» массивом можно, используя свойство ArraySegment<T>.Array.

int[] a = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

var firstSeg = new ArraySegment<int>(a, 2, 6); // первый сегмент

var secondSeg = new ArraySegment<int>(a, 4, 3); // второй сегмент

firstSeg.Array[3] = 10; // изменяем четвёртый элемент массива a

Класс System.Lazy<T> служит для поддержки отложенной инициализации объектов. Данный класс содержит булево свойство для чтения IsValueCreated и свойство для чтения Value типа T. Использование Lazy<T> позволяет задержать создание объекта до первого обращения к свойству Value. Для создания объекта используется либо конструктор без параметров типа T, либо функция, передаваемая конструктору Lazy<T>.

Lazy<Student> lazy = new Lazy<Student>();

Console.WriteLine(lazy.IsValueCreated); // false

Student s = lazy.Value;

Console.WriteLine(lazy.IsValueCreated); // true

Делегаты

Делегат – это пользовательский тип, который инкапсулирует метод. В C# делегат объявляется с использованием ключевого слова delegate. При этом указывается имя делегата, тип и сигнатура инкапсулируемого метода:

public delegate double Function(double x);

public delegate void Subroutine(int i);

Делегат – самостоятельный пользовательский тип, он может быть как вложен в другой пользовательский тип (класс, структуру), так и объявлен отдельно. Так как делегат – это тип, то нельзя объявить в одной области видимости два делегата с одинаковыми именами, но разной сигнатурой.

После объявления делегата можно объявить переменные этого типа:

Function F;

Subroutine S;

Переменные делегата инициализируются конкретными методами при использовании конструктора делегата с одним параметром – именем метода (или именем другого делегата). Если делегат инициализируется статическим методом, требуется указать имя класса и имя метода, для инициализации экземплярным методом указывается объект и имя метода. При этом метод должен обладать подходящей сигнатурой:

F = new Function(ClassName.SomeStaticFunction);

S = new Subroutine(obj.SomeInstanceMethod);

Для инициализации делегата можно использовать упрощённый синтаксис – достаточно указать имя метода без применения new().

F = ClassName.SomeStaticFunction;

S = obj.SomeInstanceMethod;

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

Приведём пример работы с делегатами. Создадим класс, содержащий метод расширения для трансформации массива целых чисел:

public static class ArrayHelper

{

public static int[] Transform(this int[] data, Transformer f)

{

var result = new int[data.Length];

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

{

result[i] = f(data[i]);

}

return result;

}

}

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

public delegate int Transformer(int x);

Создадим класс, который использует ArrayHelper и Transformer:

public class MainClass

{

public static int TimesTwo(int i) { return i * 2; }

public int AddFive(int i) { return i + 5; }

public static void Main()

{

int[] a = {1, 2, 3};

Transformer t = TimesTwo;

a = a.Transform(t);

var c = new MainClass();

a = a.Transform(c.AddFive);

}

}

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

// два класса, связанных наследованием

public class Person {... }

public class Student: Person {... }

// делегат для обработки

public delegate Person Register(Person p);

// вспомогательный класс с методом обработки

public class C

{

public static Student M(object o) { return new Student(); }

}

// присваивание возможно благодаря ко- и контравариантности

Register f = C.M;

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

public delegate TResult Transformer<in T, out TResult>(T x);

public static class ArrayHelper

{

public static TResult[] Transform<T, TResult>(this T[] data,

Transformer<T, TResult> f)

{

var result = new TResult[data.Length];

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

{

result[i] = f(data[i]);

}

return result;

}

}

Ключевой особенностью делегатов является то, что они могут инкапсулировать не один метод, а несколько. Подобные объекты называются групповыми делегатами. При вызове группового делегата срабатывает вся цепочка инкапсулированных в нем методов.

Групповой делегат объявляется таким же образом, как и обычный. Затем создаётся несколько объектов делегата, все они связываются с некоторыми методами. После этого используется операция + для объединения делегатов в один групповой делегат. Если требуется удалить метод из цепочки группового делегата, используется операция –. Если из цепочки удаляют последний метод, результатом будет значение null.

Приведём пример использования группового делегата типа Transformer:

int[] a = {1, 2, 3};

var c = new MainClass();

Transformer<int, int> x = TimesTwo, y = c.AddFive, z;

z = x + y; // z – групповой делегат, содержит две функции

a.Transform(z);

Любой пользовательский делегат можно рассматривать как наследник класса System.MulticastDelegate, который, в свою очередь, наследуется от класса System.Delegate. Рассмотрим элементы System.Delegate:

– Перегруженный статический метод CreateDelegate() позволяет создавать делегаты на основе информации о типе и методе.

– Метод Clone() создаёт копию объекта-делегата.

– Статический метод Combine() объединяет в групповой делегат два объекта-делегата или массив таких объектов.

– Статические методы Remove() и RemoveAll() удаляют указанный объект-делегат из группового делегата[16].

– Метод GetInvocationList() возвращает массив инкапсулированных объектов-делегатов.

– Свойство Method позволяет получить информацию о методе, инкапсулированном объектом-делегатом.

– Свойство Target содержит ссылку на объект, связанный с инкапсулированным методом (для экземплярных методов или методов расширения).

В пространстве имён System объявлено несколько полезных универсальных делегатов. Имеются делегаты для представления функций и действий, содержащих от нуля до шестнадцати аргументов – Func<> и Action<>, делегаты для функций конвертирования Converter<in TInput, out TOutput>, сравнения Comparison<in T> и предиката Predicate<in T>.





Дата публикования: 2015-11-01; Прочитано: 279 | Нарушение авторского права страницы | Мы поможем в написании вашей работы!



studopedia.org - Студопедия.Орг - 2014-2024 год. Студопедия не является автором материалов, которые размещены. Но предоставляет возможность бесплатного использования (0.01 с)...