Я никогда не понимаю, почему нам нужны делегаты? Я знаю, что они являются неизменяемыми ссылочными типами, которые содержат ссылку на метод, но почему мы не можем просто вызвать метод напрямую, а не вызвать его через делегат?
Спасибо
Я никогда не понимаю, почему нам нужны делегаты? Я знаю, что они являются неизменяемыми ссылочными типами, которые содержат ссылку на метод, но почему мы не можем просто вызвать метод напрямую, а не вызвать его через делегат?
Спасибо
Конечно, вы можете вызывать метод непосредственно на объекте, но учитывать следующие сценарии:
Select
.Простой ответ: код, необходимый для выполнения действия, не знает метода вызова при его написании. Вы можете только вызвать метод напрямую, если знаете, во время компиляции, какой метод вызывать, не так ли? Поэтому, если вы хотите абстрагироваться от идеи "выполнить действие X в соответствующее время", вам нужно некоторое представление о действии, так что метод, вызывающий действие, не должен заранее знать точную реализацию.
Например:
Enumerable.Select
в LINQ не может знать проекцию, которую вы хотите использовать, если вы не сообщите ейButton
не знал, что вы хотите, чтобы действие было, когда пользователь нажимает на него.Это может помочь вам подумать о том, что делегаты являются как интерфейсы с одним методом, но с большим количеством синтаксиса языка, чтобы сделать их простыми в использовании, и фанковая поддержка асинхронного выполнения и многоадресной рассылки.
Поскольку у вас может не быть еще написанного метода или вы разработали свой класс таким образом, чтобы пользователь мог решить, какой метод (который пользователь написал), пользователь хочет выполнить ваш класс.
Они также делают некоторые проекты более чистыми (например, вместо оператора switch, где вы вызываете разные методы, вы вызываете переданный делегат), и проще понять и разрешить расширение кода без его изменения (подумайте OCP).
Делегаты также являются основой системы событий - запись и регистрация обработчиков событий без делегатов будет намного сложнее, чем с ними.
Обратитесь к другим делегатам Action
и Func
в Linq - это вряд ли будет так же полезно без них.
Сказав это, никто не заставляет вас использовать делегатов.
Подумайте о указателях функций C/С++ и о том, как вы обрабатываете функции обработки событий javascript как "данные" и передаете их. В языке Delphi также существует процедурный тип. За кулисами, делегат С# и лямбда-выражения, и все эти вещи по существу являются одной и той же идеей: код как данные. И это составляет основу для функционального программирования.
Все, что может быть сделано с делегатами, может выполняться без них, но делегаты предоставляют гораздо более чистый способ их выполнения. Если у кого-то не было делегатов, нужно было бы определить интерфейс или абстрактный базовый класс для каждой возможной сигнатуры функции, содержащей функцию Invoke (соответствующие параметры) и определить класс для каждой функции, которая должна была быть вызвана псевдо-делегатами. Этот класс наследует соответствующий интерфейс для сигнатуры функции, будет содержать ссылку на класс, содержащий функцию, которую он должен был представлять, и метод, реализующий Invoke (соответствующие параметры), который вызовет соответствующую функцию в классе, к которому он принадлежит ссылка. Если класс Foo имеет два метода Foo1 и Foo2, оба из которых берут один параметр, оба из которых могут быть вызваны псевдо-делегатами, создаются два дополнительных класса, по одному для каждого метода.
Без поддержки компилятора для этой методики исходный код должен быть довольно отвратительным. Если бы компилятор мог автоматически генерировать собственные вложенные классы, все могло бы быть довольно чистым. Скорость отправки для псевдо-делегатов, вероятно, обычно была бы медленнее, чем у обычных делегатов, но если псевдо-делегаты были интерфейсом, а не абстрактным базовым классом, класс, который должен только сделать псевдо-делегат для одного метода данной подписи, реализовать соответствующий интерфейс псевдоделегата; экземпляр класса затем может быть передан любому коду, ожидающему псевдо-делегата этой подписи, избегая необходимости создания дополнительного объекта. Кроме того, хотя количество классов, которые нужно было бы использовать при использовании псевдо-делегатов, было бы больше, чем при использовании "реальных" делегатов, каждому псевдо-делегату нужно было бы только один экземпляр объекта.
Вы попросили пример того, почему вы передадите функцию в качестве параметра, у меня есть идеальный вариант, и я подумал, что это может помочь вам понять, это довольно академично, но показывает использование. Скажем, у вас есть метод 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;
}
Вы можете думать о них как о конструкции, подобной указателям на функции в C/С++. Но они больше, чем в С#. Подробности.