Зачем нам нужны делегаты C # - PullRequest
56 голосов
/ 26 ноября 2010

Кажется, я никогда не понимаю, зачем нам делегаты?Я знаю, что они являются неизменяемыми ссылочными типами, которые содержат ссылку на метод, но почему мы не можем просто вызвать метод напрямую, а не вызывать его через делегат?

Спасибо

Ответы [ 8 ]

59 голосов
/ 26 ноября 2010

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

Например:

  • Enumerable.Select в LINQ не может знать проекцию, которую вы хотите использовать, если вы не скажете ее
  • Автор Button не зналчто вы хотите, чтобы действие было, когда пользователь нажимает на него
  • Если бы новый поток только когда-либо делал что-то, это было бы довольно скучно ...

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

33 голосов
/ 26 ноября 2010

Конечно, вы можете вызывать метод непосредственно на объекте, но рассмотрите следующие сценарии:

  1. Вы хотите вызывать серию методов с использованием одного делегата без записи большого количества вызовов методов.
  2. Вы хотите элегантно реализовать систему, основанную на событиях.
  3. Вы хотите вызвать два метода одинаково в сигнатуре, но находиться в разных классах.
  4. Вы хотите передать метод в качестве параметра.
  5. Вы не хотите писать много полиморфного кода, как в LINQ, вы можете обеспечить большую реализацию метода Select.
17 голосов
/ 26 ноября 2010

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

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

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

См. Различные Action и Func делегатов в Linq - вряд ли было бы так же полезно без них.

Сказав это, никто не заставляет вас использовать делегатов.

8 голосов
/ 26 ноября 2010
  1. Делегаты поддерживают события
  2. Делегаты дают вашей программе возможность выполнять методы, не зная точно, что это за методы во время компиляции.
4 голосов
/ 02 августа 2011

Все, что может быть сделано с делегатами, может быть сделано без них , но делегаты обеспечивают более чистый способ их выполнения. Если бы у кого-то не было делегатов, нужно было бы определить интерфейс или абстрактный базовый класс для каждой возможной сигнатуры функции, содержащей функцию Invoke (соответствующие параметры), и определить класс для каждой функции, которая должна была вызываться псевдо-делегатами. Этот класс наследовал бы соответствующий интерфейс для сигнатуры функции, содержал бы ссылку на класс, содержащий функцию, которую она должна была представлять, и метод, реализующий Invoke (соответствующие параметры), который вызывал бы соответствующую функцию в классе, к которому она относится ссылка. Если в классе Foo есть два метода Foo1 и Foo2, каждый из которых принимает один параметр, оба из которых могут вызываться псевдо-делегатами, то будет создано два дополнительных класса, по одному для каждого метода.

Без поддержки компилятором этой техники исходный код был бы довольно отвратительным. Если бы компилятор мог автоматически генерировать правильные вложенные классы, все могло бы быть довольно чисто. Скорость отправки для псевдо-делегатов, вероятно, будет, как правило, медленнее, чем с обычными делегатами, но если бы псевдо-делегаты были интерфейсом, а не абстрактным базовым классом, то класс, который должен создать псевдо-делегат только для одного метода данной подписи, реализовать соответствующий интерфейс псевдо-делегата; затем экземпляр класса может быть передан любому коду, ожидающему псевдо-делегата этой подписи, избегая необходимости создавать дополнительный объект. Кроме того, хотя количество классов, которые могут понадобиться при использовании псевдо-делегатов, будет больше, чем при использовании «настоящих» делегатов, каждый псевдо-делегат должен будет содержать только один экземпляр объекта.

3 голосов
/ 26 ноября 2010

Подумайте о указателях функций C / C ++ и о том, как вы воспринимаете функции обработки событий javascript как «данные» и передаете их.В языке Delphi также существует процедурный тип.За кулисами, C # делегат и лямбда-выражения, и все эти вещи по сути одна и та же идея: код как данные.И это составляет самую основу для функционального программирования.

1 голос
/ 19 апреля 2013

Вы попросили привести пример того, почему вы передали бы функцию в качестве параметра. У меня есть идеальный вариант, и вы подумали, что это может помочь вам понять, это довольно академично, но показывает применение. Скажем, у вас есть метод ListResults () и метод getFromCache (). Вместо этого вы можете выполнить множество проверок, если кэш равен нулю и т. Д., Вы можете просто передать любой метод getCache, а затем вызывать его, только если кэш пуст внутри метода getFromCache:

_cacher.GetFromCache(delegate { return ListResults(); }, "ListResults");

public IEnumerable<T> GetFromCache(MethodForCache item, string key, int minutesToCache = 5)
    {
        var cache = _cacheProvider.GetCachedItem(key);
        //you could even have a UseCache bool here for central control
        if (cache == null)
        {
            //you could put timings, logging etc. here instead of all over your code
            cache = item.Invoke();
            _cacheProvider.AddCachedItem(cache, key, minutesToCache);
        }            

        return cache;
    }
0 голосов
/ 26 ноября 2010

Вы можете думать о них как о конструкции, похожей на указатели на функции в C / C ++. Но они больше, чем в C #. Подробнее .

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