Примеры с делегатами в C#

Приветствую всех, сегодня хочу еще раз поговорить о делегатах. Давайте вспомним что такое делегат. По сути, это объект, который хранит ссылку на некий метод, и может этот метод вызвать при необходимости. Точнее мы можем вызвать метод через этот объект. И особенность использования делегатов в том, что при объявлении ссылки на объект-делегат мы можем и не знать, на какой конкретно метод, будет ссылаться этот объект. Мы только знаем, что целевой метод должен иметь определенного вида список параметров и тип возвращаемого значения. 

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

delegate тип_значения ИмяТипаДелегата(список_параметров);

Как видите, объявление типа делегата, отчасти, похоже на объявление метода, но в самом начале, указывается ключевое слово delegate, а после списка аргументов, нет тела как у метода, вместо которого ставит символ «;».

Обратите внимание, объект-делегат может ссылаться только на такие методы, чья сигнатура (список параметров и тип возвращаемого значения, в данном контексте) полностью совпадает с объявлением типа этого делегата. Но ссылаться делегат может как на статические, так и на обычные методы классов.

Пример:

/Объявляем делегат, по сути - тип делегатов
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». А вызов метода происходит следующим образом:

//Вызов метода Sum через делегат sumDelegat
double sumResult = sumDelegat(24.5, 21.4);
//Объявляем делегат, по сути - тип делегатов
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);
    }
}

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

Давайте сразу рассмотрим фрагмент кода, в котором представлено объявление типа делегатов и класс, описывающий объекты-коллекции:

//Делегат, для использования в классе 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:

//Вызывает метод, на который ссылается делегат для всех элементов коллекции
public void DoOperation()
{
    //Если делегат задан
    if (operation != null)
    {
        //Перебрать все элементы коллекции
        for (int i = 0; i < collection.Count; i++)
        {
            //Выполнить операцию над текущим элементом
            collection[i] = operation(collection[i]);
        }
    }
}

В данном методе, перебираются все элементы коллекции, и над каждым выполняется вызов метода, на который ссылается делегат operation .

//Основной класс программы
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  который мог бы делать и какие-то другие действия.

 

Обновлено: 25.10.2018 — 12:03

2 комментария

Оставить комментарий
  1. НикитаЖе

    Всем привет друзья. Я не понял. Зачем Мне делегать чтобы вызвать два разных статических метода разница и сумма. В чем проблема взять и не вызвать метод и передать туда нужные параметры?

    1. Так как делегаты упрощают работу, сокращая код. Вы можете выстроить цепочку из методов с помощью делегатов, и вызвать по необходимости. Так же можно применять делегаты в качестве аргументов, приведу пример, у вас метод может работать с большим набором данных, которые он должен обработать внутри себя. Вы можете поочередно вызывать методы, а можете одним делегатом передать как аргумент и обработать внутри метода, что сократит код в несколько десятков строк кода, и повысит производительность. Что бы понять преимущества делегатов надо смотреть примеры.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *