Приветствую всех, сегодня хочу еще раз поговорить о делегатах. Давайте вспомним что такое делегат. По сути, это объект, который хранит ссылку на некий метод, и может этот метод вызвать при необходимости. Точнее мы можем вызвать метод через этот объект. И особенность использования делегатов в том, что при объявлении ссылки на объект-делегат мы можем и не знать, на какой конкретно метод, будет ссылаться этот объект. Мы только знаем, что целевой метод должен иметь определенного вида список параметров и тип возвращаемого значения.
Для создания делегата, нужно сначала определить его тип. Как и в случае классов, мы создаем некий шаблон, в соответствии с которым, буде в дальнейшем создавать конкретные экземпляры. Тип делегата определяется по следующему правилу:
1 |
delegate тип_значения ИмяТипаДелегата(список_параметров); |
Как видите, объявление типа делегата, отчасти, похоже на объявление метода, но в самом начале, указывается ключевое слово delegate, а после списка аргументов, нет тела как у метода, вместо которого ставит символ «;».
Обратите внимание, объект-делегат может ссылаться только на такие методы, чья сигнатура (список параметров и тип возвращаемого значения, в данном контексте) полностью совпадает с объявлением типа этого делегата. Но ссылаться делегат может как на статические, так и на обычные методы классов.
Пример:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
/Объявляем делегат, по сути - тип делегатов delegate double DoubleDelegat(double aFirstArg, double aSecondArg); //Основной класс программы class Program { //Статический метод, возвращающий сумму двух аргументов static double Sum(double aFirstSumArg, double aSecondSumArg) { return aFirstSumArg + aSecondSumArg; } //Главный метод программы (точка входа) static void Main(string[] args) { //Создаем экземпляр делегата DoubleDelegat (ссылающийся на метод Sum) DoubleDelegat sumDelegat = new DoubleDelegat(Sum); } } |
Как видите, создание делегата очень похоже на создание обычного объекта, но при создании объекта-делегата, мы указываем метод, на который ссылается делегат. В данном случае, это метод «Sum». А вызов метода происходит следующим образом:
1 2 |
//Вызов метода Sum через делегат sumDelegat double sumResult = sumDelegat(24.5, 21.4); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
//Объявляем делегат, по сути - тип делегатов delegate double DoubleDelegat(double aFirstArg, double aSecondArg); //Основной класс программы class Program { //Статический метод, возвращающий сумму двух аргументов static double Sum(double aFirstSumArg, double aSecondSumArg) { return aFirstSumArg + aSecondSumArg; } //Статический метод, возвращающий разность двух аргументов static double Sub(double aFirstSumArg, double aSecondSumArg) { return aFirstSumArg - aSecondSumArg; } //Главный метод программы (точка входа) static void Main(string[] args) { //Создаем экземпляр делегата DoubleDelegat (ссылающийся на метод Sum) DoubleDelegat sumDelegat = new DoubleDelegat(Sum); //Вызов метода Sum через делегат sumDelegat double sumResult = sumDelegat(24.5, 21.4); //Вывод результата в консоль Console.WriteLine("Результат работы sumResult с методом Sum: " + sumResult); //Создаем экземпляр делегата DoubleDelegat (ссылающийся на метод Sum) DoubleDelegat subDelegat = new DoubleDelegat(Sub); //Вызов метода Sum через делегат sumDelegat double subResult = subDelegat(24.5, 21.4); //Вывод результата в консоль Console.WriteLine("Результат работы subResult с методом Sub: " + subResult); } } |
Таким образом, можно сделать вывод, что при объявлении типа делегата, мы указываем только прототип методов, которые этот делегат может вызывать, а что эти методы будут делать, мы можем даже не догадываться.
Давайте сразу рассмотрим фрагмент кода, в котором представлено объявление типа делегатов и класс, описывающий объекты-коллекции:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
//Делегат, для использования в классе DoubleCollection delegate double DoubleOperation(double anAgr); //Коллекция элементов типа double class DoubleCollection { //Добавляет элемент в коллекцию public void Add(double anItem) { collection.Add(anItem); } //Удаляет элемент из коллекции (по индексу) public void RemoveAt(int anIndex) { collection.RemoveAt(anIndex); } //Вызывает метод, на который ссылается делегат для всех элементов коллекции public void DoOperation() { //Если делегат задан if (operation != null) { //Перебрать все элементы коллекции for (int i = 0; i < collection.Count; i++) { //Выполнить операцию над текущим элементом collection[i] = operation(collection[i]); } } } //Устанавливает или возвращает значение делегата operation public DoubleOperation Operation { get { return operation; } set { operation = value; } } //Индексатор (возвращает или устанавливает значение элемента по индексу) public double this[int anIndex] { get { return collection[anIndex]; } set { collection[anIndex] = value; } } //Возвращает количество элементов public int Count { get { return collection.Count; } } //Хранилище элементов private List<double> collection = new List<double>(); //Делегат, для хранения ссылки на метод, который укажет пользователь класса private DoubleOperation operation = null; } |
В начале программы объявляется тип делегатов DoubleOperation, делегаты такого типа ссылаются на методы, принимающие один аргумент типа double и возвращающие значение такого же типа. В классе DoubleCollection объявляется поле operation, которое является ссылкой на делегат, типа DoubleOperation. Значение полю operation задается через свойство Operation . Рассмотрим подробнее код метода DoOperation:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
//Вызывает метод, на который ссылается делегат для всех элементов коллекции public void DoOperation() { //Если делегат задан if (operation != null) { //Перебрать все элементы коллекции for (int i = 0; i < collection.Count; i++) { //Выполнить операцию над текущим элементом collection[i] = operation(collection[i]); } } } |
В данном методе, перебираются все элементы коллекции, и над каждым выполняется вызов метода, на который ссылается делегат operation .
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
//Основной класс программы class Program { //Возвращает значение операнда, увеличенное на 13 процентов static double SomeOperation(double aValue) { return aValue * 1.13; } //Главный метод программы static void Main(string[] args) { //Создание коллекции DoubleCollection someCollection = new DoubleCollection(); //Добавление в неё элементов someCollection.Add(124.4); someCollection.Add(120.8); someCollection.Add(118.1); someCollection.Add(128.9); someCollection.Add(111.3); //Создание делегата на основе метода SomeOperation и присваивание его коллекции someCollection.Operation = new DoubleOperation(SomeOperation); //Выполнение операции someCollection.DoOperation(); //Вывод результата в консоль for (int i = 0; i < someCollection.Count; i++) Console.WriteLine(someCollection[i]); } } |
Пользователю класса DoubleCollection нужно просто позаботиться о создании метода SomeOperation который мог бы делать и какие-то другие действия.
Всем привет друзья. Я не понял. Зачем Мне делегать чтобы вызвать два разных статических метода разница и сумма. В чем проблема взять и не вызвать метод и передать туда нужные параметры?
Так как делегаты упрощают работу, сокращая код. Вы можете выстроить цепочку из методов с помощью делегатов, и вызвать по необходимости. Так же можно применять делегаты в качестве аргументов, приведу пример, у вас метод может работать с большим набором данных, которые он должен обработать внутри себя. Вы можете поочередно вызывать методы, а можете одним делегатом передать как аргумент и обработать внутри метода, что сократит код в несколько десятков строк кода, и повысит производительность. Что бы понять преимущества делегатов надо смотреть примеры.