Делегаты в С#

У меня возникли проблемы с пониманием того, как работают делегаты на С#. У меня много примеров кода, но я все еще не мог понять его правильно.

Может кто-нибудь объяснить это мне в "простом английском"? Конечно! примеры кода помогут, но я думаю, мне нужно больше описать, как и почему он работает.

EDIT:

Ну, вопрос в том, почему работают делегаты? Что такое "блок-схема" всего процесса?

Каковы предпосылки использования делегатов?

Надеюсь, это сделает вопрос более ясным.

Ответ 1

Один способ подумать о делегате - это как ссылка на функцию. Например, скажем, у вас есть кнопка в окне, и вы хотите, чтобы что-то произошло, когда нажата кнопка. Вы можете присоединить делегата к событию Click кнопки, и всякий раз, когда пользователь нажимает на эту кнопку, ваша функция будет выполнена.

class MyWindow : Window
{
    Button _button;

    public MyWindow()
    {
        _button = new Button();
        // place the button in the window
        _button.Click += MyWindow.ButtonClicked;
    }

    static void ButtonClicked(object sender, RoutedEventArgs e)
    {
        MessageBox.Show("Button Clicked");
    }
}

Обратите внимание на то, как я делаю ButtonClicked статической функцией - я хочу еще раз указать на нестатические функции. Предположим, что ButtonClicked является нестатическим членом:

class MyWindow : Window
{
    Button _button;
    int _numClicked = 0;

    public MyWindow()
    {
        this._button = new Button();
        // place the button in the window
        this._button.Click += this.ButtonClicked;
    }

    void ButtonClicked(object sender, RoutedEventArgs e)
    {
        this._numClicked += 1;
        MessageBox.Show("Button Clicked " + this._numClicked + " times");
    }
}

Теперь делегат содержит ссылку на функцию "ButtonClicked" и экземпляр "this", на который вызывается метод. Экземпляр "this" в конструкторе MyWindow и "this" в ButtonClicked совпадают.

Это конкретный случай понятия, известного как замыкания, которое позволяет "сохранять" состояние - текущий объект, локальные переменные и т.д. - при создании делегата. В приведенном выше примере мы использовали "this" из конструктора в делегате. Мы можем сделать больше, чем это:

class MyWindow : Window
{
    Button _button;
    int _numClicked = 0;

    public MyWindow(string localStringParam)
    {
        string localStringVar = "a local variable";
        this._button = new Button();
        // place the button in the window
        this._button.Click += new RoutedEventHandler(
            delegate(object sender, RoutedEventArgs args)
            {
                this._numClicked += 1;
                MessageBox.Show("Param was: " + localStringParam + 
                     " and local var " + localStringVar +
                     " button clicked " + this._numClicked + " times");
            });
    }
}

Здесь мы создали анонимный делегат - функцию, которая не имеет явного имени. Единственный способ ссылаться на эту функцию - использовать объект делегирования RoutedEventHandler. Кроме того, эта функция существует в области конструктора MyWindow, поэтому она может обращаться ко всем локальным параметрам, переменным и экземпляру-члену "this". Он продолжит удерживать ссылки на локальные переменные и параметры даже после выхода из конструктора MyWindow.

В качестве дополнительной заметки делегат также будет содержать ссылку на экземпляр объекта - "this" - даже после удаления всех других ссылок на удаленный класс. Поэтому, чтобы убедиться, что класс является сборкой мусора, все делегаты для метода нестатического члена (или делегатов, созданных в пределах одного) должны быть удалены.

Ответ 2

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

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

Еще один образец кода, который будет полным:

   delegate void ADelegate();  // the delegate type

   void Foo() { ... }   // a compatible method
   void Bar() { ... }   // a compatible method

   void Main()
   {
      ADelegate funcPtr;  // a delegate variable

      if (aCondition)
        funcPtr = Foo;  // note: _not_ Foo(), Foo is not executed here
      else
        funcPtr = Bar;

      funcPtr(); // calls Foo or Bar depending on aCondition
   }

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

  delegate int Compare(MyClass a, MyClass b);  // the delegate type

  void int CompareUp(MyClass a, MyClass b) { ... }   
  void int CompareDn(MyClass a, MyClass b) { ... }   

  void Sort(MyClass[] data, Compare comparer) { ... }

И вы, вероятно, знаете события, которые являются (особым видом) свойствами, основанными на делегатах.

Ответ 3

Делегаты - это указатели на функции, определенные где-то.

Предположим, что у вас есть класс BankAccount, и вы должны отправлять электронное письмо клиенту, когда его баланс составляет менее 100 долларов США. Тогда естественная тенденция состоит в том, чтобы добавить чек в настройщике свойств Баланса, чтобы узнать, равен ли баланс клиента менее 100 долларов США, и если да, инициируйте электронное письмо. Но этот дизайн не является гибким.

Недостатки вышеуказанного подхода:

В будущем определенно будет требование отправить текстовое сообщение вместо электронного письма клиенту. Немногие клиенты выбирают как электронную почту, так и текстовое сообщение. Поэтому всякий раз, когда вам нужно уведомить клиента о любых изменениях, вы перейдете и измените класс BankAccount. Это является нарушением принципов твердотельного проектирования OPEN FOR EXTENSION и CLOSED FOR MODICICATION.

Альтернативное решение с использованием DELEGATES:

  • Определите метод NotifyCustomer(), который касается отправки уведомления клиенту о низком балансе за пределами класса BankAccount.

  • Измените класс BankAccount, чтобы определить делегат и принять значение для него в конструкторах.

  • При создании класса BankAccount перейдите в метод NotifyCustomer(), созданный на шаге 1.

  • В наборе Баланс баланса BankAccount проверьте, равен ли остаток менее 100 долларов США. Если это так, вызовите делегата.

  • Вызывается метод NotifyCustomer(), определенный вне класса BankAccount, в результате чего отправляется уведомление как определено.

В будущем, если есть новый способ уведомления клиента, то в классе BankAccount изменений не требуется.

Плюсы дизайна с использованием делегатов:

  • LOOSE COUPLING. Класс BankAccount не знает о жестко закодированной логике, уведомляющей клиента.

  • Выполняется принцип ОТКРЫТЬ ДЛЯ РАСШИРЕНИЯ И ЗАКРЫТО ДЛЯ МОДИФИКАЦИИ. Всякий раз, когда среда уведомления клиента изменяется, вам не требуется менять класс BankAccount. Итак, теперь вы с гордостью можете сказать, что ваш проект класса BankAccount следует принципу дизайна.

Если вы хотите больше узнать о делетете в примере, прочитайте Что такое делегаты и почему они нам нужны.

Ответ 4

Его принцип инверсии. Обычно вы пишете код, который вызывает метод, и метод, который вы вызываете, известен в то время, когда вы пишете код. Делегаты позволяют анонимно вызывать методы. То есть вы не знаете фактический метод, который вызывается при запуске программы.

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

Код обработки может быть записан в предположении определенных вещей о данных, которые не зависят от структуры хранилища данных. Таким образом, мы можем изменить структуру хранилища данных с меньшим беспокойством о влиянии процессов на данные.

Ответ 5

Вы можете представить делегатов как способ просмотра кода как данных. Если вы создаете делегат, это тип. Переменные этого типа могут указывать на определенные методы (которые соответствуют определению делегата).

Это означает, что вы можете обрабатывать кусок кода как данные и, например, передавать его методу. Поскольку делегаты указывают на код (или null), вы также можете вызвать код, который он указывает через переменную.

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

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

Ответ 6

Делегат - это тип ссылок, который вызывает один или несколько методов (ов) через экземпляр делегата. Он содержит ссылку на методы. Отношения могут использоваться для обработки (вызова/вызова) нескольких методов в одном событии. Делегаты могут использоваться для определения асинхронных методов. Вот пример для делегата Сначала мы создаем класс. В котором мы объявляем делегат. И мы создаем метод внутри класса, в котором мы вызываем делегата.

public class simpleinterest
{
    public delegate void intcal(double i);  //declare delegate
    public event intcal interest; //create delegate object
    public void calculate(double p, double n,double r)
    {
       interest(p*n*r/100);   //invoke delegate
    }

}

Внутри нашей программы мы делаем сопоставление. Это мы укажем, какое событие вызывается при вызове делегата.

    private void btn_Click(object sender, RoutedEventArgs e)
    {
        simpleinterest s1 = new simpleinterest();
        s1.interest+=new simpleinterest.intcal(s1_interest);//mapping

        s1.calculate(1000,3,10);

    }
    void s1_interest(double r)
    {
         MessageBox.Show("Amount:" + r.ToString());

    }

Ответ 7

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

public delegate void HelloFunctionDelegate(string message);
class Program
{
 static void Main()
{
HelloFunctionDelegate del = new HelloFunctionDelegate(Hello);
del("Hello from Delegate");
}
public static void Hello(string strMessage)
{
 Console.WriteLine(strMessage);
}
}

Выход Hello из делегата

Ответ 8

Delagates in С#: определяет подпись метода, который он может вызывать. Другими словами, мы можем сказать, что он обертывает ссылку метода, который он может вызвать. Ниже представлены команды делегатов:

  • Он обеспечивает механизм для реализации функций обратного вызова в .NET framework.
  • Он обеспечивает возможность вызова нескольких методов последовательно.
  • Он имеет возможность реализовать асинхронный вызов метода.

Он поддерживает как статические, так и методы экземпляра.

Ниже приводится объяснение того, как он работает внутри.

//Вот объявление делегатов.

public delegate void DisplayNamme(string name);

во время выполнения CLR создает класс для делегатов, как показано ниже.

public class DisplayNamme : System.MulticastDelegate{

   // It is a contructor
   public DisplayNamme(Object @object, IntPtr method);

   // It is the method with the same prototype as defined in the source code. 
   public void Invoke(String name);

// This method allowing the callback to be asynchronouslly.

 public virtual IAsyncResult BeginInvoke(String name, 
 AsyncCallback callback, Object @object); 

 public virtual void EndInvoke(IAsyncResult result); 

}

Мы видим это через инструмент ILDasm.exe . Используйте этот инструмент, чтобы разбить DLL.

Конструктор имеет два параметра: IntPrt ссылается на имя метода, который передается функции, а @object ссылается на ссылку объекта, который неявно передается конструктору.

CLR использует метод Invoke для вызова метода обратного вызова.

Ниже приведена реализация метода обратного вызова с использованием делегатов.

// Declare Delegates
    public delegate void DisplayNamme(string name);
    class Program
    {
       public static void getName(string name)
        {
            Console.WriteLine(name);
        }
       public static void ShowName(DisplayNamme dn, string name)
        {
        // callback method calling. We can call it in two ways. 
           dn(name);
          // or explicitly
            dn.Invoke(name);
    }
      static void Main(string[] args)
        {
            DisplayNamme delDN = getName;
            Program.ShowName(delDN, "CallBack");
            Console.ReadLine();
        }
    }

Ответ 9

1) Сначала вы должны понять, почему/когда вам нужен делегат, в чем проблема, которую он разрешает.

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

Иммуните компонент Grid, который позволяет разработчику настраивать способ отображения каждого столбца. Например, вы хотите записать значение красного цвета, когда оно под номером 0.

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

2) Затем вам нужно понять, как работает делегат

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

Это класс сетки:

// the grid let the programmer that will use it to customize the output
public class Grid{

    // 1) First I declare only the interface of the delegate
    public delegate String ValueFormatterDelegate(String v);

    // 2) I declare a handler of the implementation of the delegate
    public ValueFormatterDelegate ValueFormatterHandler; 

    // 3) I call the handler inside the Print method
    public void Print(String x){
        Console.WriteLine( ValueFormatterHandler.Invoke(x) );
    }

}

//1) Сначала объявляю только интерфейс делегата public delegate String ValueFormatterDelegate (String v);

Обратите внимание, что это обычный метод, но:

  • у него есть ключевое слово delegate
  • у него нет реализации

Таким образом, я говорю: "метод, который будет форматировать вывод, имеет этот интерфейс: он будет вводить строку в качестве входных данных и выводит строку"

Он помнит мне определение метода интерфейса.

//2) Объявляю обработчик реализации делегата public ValueFormatterDelegate ValueFormatterHandler;

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

//3) Я вызываю обработчик внутри метода печати public void Print (String x) {   Console.WriteLine(ValueFormatterHandler.Invoke(x)); }

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

ValueFormatterHandler имеет тип ValueFormatterDelegate и ValueFormatterDelegate является делегатом объявления и .Invoke - это метод типа делегата

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

using System;

public class Program{
    public static void Main(){

        var printer = new Printer();

        // METHOD 1 : link to a named method
        // here i link the handler of the delegate to a real method
        // the FormatXXX is a static method defined at the ed of this code
        printer.ValueFormatter = FormatXXX;

        // when i call Print("hello")
        printer.Print("hello"); // XXhelloXX

        // METHOD 2 : anonimous method
        // think at this like a method but without a name
        // FormatYY (String x ){ return "YY"+x+"YY"; };
        //  become
        // delegate (String x ){ return "YY"+x+"YY"; };
        printer.ValueFormatter = delegate (String x ){ return "YY"+x+"YY"; };
        printer.Print("hello"); // YYhelloYY

        // METHOD 3 : anonimous method using lambda
        // as you can note the type of parameter x is inferred from the delegate declaration
        // public delegate String ValueFormatterDelegate(String v);
        printer.ValueFormatter = (x)=>"KK" + x + "KK";

    }

    public static String FormatXXX(String y){
        return "XX"+ y +"XX";
    }

}

Ответ 10

Делегат является переменной ссылочного типа, которая указывает ссылку на метод. Все делегаты получены из класса System.Delegate. Например, в Windows Forms или WPF событие метода работает с концепцией делегатов. Это пример использования delagates в С# Введение в делегаты на С#