Подтвердить что ты не робот

Делегаты, почему?

Возможные дубликаты:
Когда вы будете использовать делегатов на С#? Цель делегатов

Я видел много вопросов относительно использования делегатов. Я все еще не понимаю, где и ПОЧЕМУ вы бы использовали делегатов вместо прямого вызова метода.

Я слышал эту фразу много раз: "Затем объект-делегат может быть передан в код, который может вызвать ссылочный метод, без необходимости знать во время компиляции, какой метод будет вызван".

Я не понимаю, как это утверждение правильно.

Я написал следующие примеры. Скажем, у вас есть 3 метода с одинаковыми параметрами:

   public int add(int x, int y)
    {
        int total;
        return total = x + y;
    }
    public int multiply(int x, int y)
    {
        int total;
        return total = x * y;
    }
    public int subtract(int x, int y)
    {
        int total;
        return total = x - y;
    }

Теперь я объявляю делегата:

public delegate int Operations(int x, int y);

Теперь я могу сделать еще один шаг: объявить обработчик для использования этого делегата (или вашего делегата напрямую)

Вызов делегата:

MyClass f = new MyClass();

Operations p = new Operations(f.multiply);
p.Invoke(5, 5);

или вызов с помощью обработчика

f.OperationsHandler = f.multiply;
//just displaying result to text as an example
textBoxDelegate.Text = f.OperationsHandler.Invoke(5, 5).ToString();

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

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

Может кто-нибудь, пожалуйста, дайте мне реальную ситуацию в мире? Я полностью смущен.

4b9b3361

Ответ 1

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

В принципе, делегаты экономят вас от печатания.

Например:

class Person
{
    public string Name { get; }
    public int Age { get; }
    public double Height { get; }
    public double Weight { get; }
}

IEnumerable<Person> people = GetPeople();

var orderedByName = people.OrderBy(p => p.Name);
var orderedByAge = people.OrderBy(p => p.Age);
var orderedByHeight = people.OrderBy(p => p.Height);
var orderedByWeight = people.OrderBy(p => p.Weight);

В приведенном выше коде p => p.Name, p => p.Age и т.д. - все лямбда-выражения, которые вычисляют делегаты Func<Person, T> (где T - string, int, double и double, соответственно).

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

public static IEnumerable<Person> OrderByName(this IEnumerable<Person> people);
public static IEnumerable<Person> OrderByAge(this IEnumerable<Person> people);
public static IEnumerable<Person> OrderByHeight(this IEnumerable<Person> people);
public static IEnumerable<Person> OrderByWeight(this IEnumerable<Person> people);

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

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

Ответ 3

Делегаты чрезвычайно полезны, особенно после введения linq и закрытия.

Хорошим примером является функция "Где", один из стандартных методов linq. 'Where' принимает список и фильтр и возвращает список элементов, соответствующих фильтру. (Аргумент фильтра - это делегат, который принимает T и возвращает логическое значение.)

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

Более конкретно: если взять делегата, вы можете написать это:

var result = list.Where(x => x != null);
...

вместо этого:

var result = new List<T>();
foreach (var e in list)
    if (e != null)
        result.add(e)
...

Ответ 4

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

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

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

public delegate void SimpleAction();

public class Timer {
    public Timer(int secondsBetweenActions, SimpleAction simpleAction) {}
}

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

Ответ 5

Позвольте мне привести пример. Если ваш класс предоставляет event, ему может быть назначено некоторое количество делегатов во время выполнения, которое будет вызываться, чтобы сигнализировать, что что-то произошло. Когда вы написали класс, вы понятия не имели, какие делегаты будут работать. Вместо этого это определяется тем, кто использует ваш класс.

Ответ 6

Чтобы дать конкретный пример, особенно недавнее использование делегата для меня было SendAsync() в System.Net.Mail.SmtpClient. У меня есть приложение, которое отправляет тонны и тонны электронной почты, и наблюдается заметное повышение производительности, ожидающее, когда сервер Exchange примет сообщение. Однако необходимо было зарегистрировать результат взаимодействия с этим сервером.

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

То же самое можно сказать о любом внешнем IO, в котором вы хотите продолжить приложение, не дожидаясь завершения взаимодействия. Прокси-классы для веб-сервисов и т.д. Используют это.

Ответ 7

Один пример, когда требуется delegate, - это когда вам нужно изменить элемент управления в потоке пользовательского интерфейса, и вы работаете в другом потоке. Например,

public delegate void UpdateTextBox(string data);

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    ...
    Invoke(new UpdateTextBox(textBoxData), data);
    ...

}

private void textBoxData(string data)
{
            textBox1.Text += data;
}

Ответ 8

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

private int DoSomeOperation( Operations operation )
{
    return operation.Invoke(5,5);
}

...

MyClass f = new MyClass();
Operations p = new Operations(f.multiply);
int result = DoSomeOperation( p );

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

int i = 5;
Console.Write( i + 10 );

вы видите, что значение 5 указано, поэтому вы можете просто сказать Console.Write( 5 + 10 ). Это правда в этом случае, но оно не хватает преимуществ за возможность сказать

DateTime nextWeek = DateTime.Now.AddDays(7);

вместо того, чтобы определять метод specificc DateTime.AddSevenDays() и метод AddSixDays и т.д.

Ответ 9

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

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

Ответ 11

Используя ваш пример Operations, представьте калькулятор с несколькими кнопками. Вы можете создать класс для своей кнопки, как этот

class CalcButton extends Button {
   Operations myOp;
   public CalcButton(Operations op) {
      this.myOp=op; 
   }
   public void OnClick(Event e) {
      setA( this.myOp(getA(), getB()) ); // perform the operation
   }
}

а затем, когда вы создаете кнопки, вы можете создать их с другой операцией

CalcButton addButton = new CalcButton(new Operations(f.multiply));

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

Ответ 12

Делегаты используются для решения проблемы с доступом. Когда вы хотите иметь объект foo, который должен вызвать метод frob объекта, но не имеет доступа к методу frob.

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

Например, класс Button обычно не имеет доступа к классу, определяет метод Button_click.

Итак, теперь, когда у нас есть, мы можем использовать его для многих других вещей, кроме как только событий. Асинхронные паттерны и Linq - это два примера.

Ответ 13

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

Ниже приведен мой пример того, как делегаты позволяют классу потребления изменять или дополнять поведение (эффективно добавляя "крючки", чтобы потребитель мог делать что-либо до или после критического действия и/или вообще предотвращать это поведение). Обратите внимание, что вся логика принятия решений предоставляется вне класса StringSaver. Теперь рассмотрим, что могут быть 4 разных потребителя этого класса - каждый из них может реализовать свою собственную логику Verification и Notification, или нет, если это необходимо.

internal class StringSaver
{
    public void Save()
    {
        if(BeforeSave != null)
        {
            var shouldProceed = BeforeSave(thingsToSave);
            if(!shouldProceed) return;
        }
        BeforeSave(thingsToSave);

        // do the save

        if (AfterSave != null) AfterSave();
    }

    IList<string> thingsToSave;
    public void Add(string thing) { thingsToSave.Add(thing); }

    public Verification BeforeSave;
    public Notification AfterSave;
}

public delegate bool Verification(IEnumerable<string> thingsBeingSaved);
public delegate void Notification();

public class SomeUtility
{
    public void SaveSomeStrings(params string[] strings)
    {
        var saver = new StringSaver
            {
                BeforeSave = ValidateStrings, 
                AfterSave = ReportSuccess
            };

        foreach (var s in strings) saver.Add(s);

        saver.Save();
    }

    bool ValidateStrings(IEnumerable<string> strings)
    {
        return !strings.Any(s => s.Contains("RESTRICTED"));
    }

    void ReportSuccess()
    {
        Console.WriteLine("Saved successfully");
    }
}

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