Делегаты в C # - PullRequest
       21

Делегаты в C #

20 голосов
/ 14 ноября 2009

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

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

EDIT:

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

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

Надеюсь, это прояснит вопрос.

Ответы [ 10 ]

24 голосов
/ 14 ноября 2009

Один способ думать о делегате подобен ссылке на функцию . Например, скажем, у вас есть кнопка в окне, и вы хотите, чтобы что-то происходило при нажатии кнопки. Вы можете прикрепить делегата к событию 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" - даже после удаления всех других ссылок на класс. Поэтому, чтобы обеспечить сборку мусора для класса, все делегаты нестатического метода-члена (или делегаты, созданные в области действия одного) должны быть удалены.

6 голосов
/ 31 декабря 2014

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

6 голосов
/ 14 ноября 2009

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

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

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

   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
   }

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

  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) { ... }

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

2 голосов
/ 14 ноября 2017

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

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

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

Разработчик, создавший 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) Сначала я объявляю только интерфейс делегата открытый делегат String ValueFormatterDelegate (String v);

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

  • имеет делегат ключевое слово
  • не имеет реализации

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

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

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

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

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

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

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";
    }

}
2 голосов
/ 16 сентября 2014

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

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());

    }
2 голосов
/ 14 ноября 2009

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

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

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

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

2 голосов
/ 14 ноября 2009

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

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

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

1 голос
/ 01 апреля 2017

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

  1. Предоставляет механизм реализации функции обратного вызова в .NET Framework.
  2. Предоставляет возможность вызывать несколько методов последовательно.
  3. Имеет возможность реализовывать асинхронный вызов метода.

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

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

// Вот декларация делегатов.

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 tool . Используйте этот инструмент, чтобы сломать 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();
        }
    }
1 голос
/ 15 мая 2015

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

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);
}
}

Вывод привет от делегата

0 голосов
/ 23 апреля 2016

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...