Являются ли делегаты не только сокращенными интерфейсами?

Предположим, что имеем:

interface Foo 
{
 bool Func(int x);
}

class Bar: Foo
{
  bool Func(int x)
  {
   return (x>0);
  }  
}

class Baz: Foo
{
  bool Func(int x)
  {
   return (x<0);
  }  
}

Теперь мы можем бросить Bar and Baz в качестве Foos и вызвать их методы Func.

Делегаты немного упрощают это:

delegate bool Foo(int x);

bool Bar(int x)
{
 return (x<0);
}

bool Baz(int x)
{
 return (x>0);
}

Теперь мы можем бросить вокруг Бар и Баз в качестве делегатов Foo.

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

0
добавлено отредактировано
Просмотры: 154

10 ответы

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

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

Take a look at the List<> class with the Find method. Now you get to define what determines if something is a match or not, without requiring items contained in the class to implement IListFindable or something similar.

0
добавлено

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

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

0
добавлено

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

0
добавлено

Можно подумать о делегатах как интерфейсе для метода, который определяет, какие аргументы и тип возвращаемого метода должен иметь соответствующий делегат

0
добавлено

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

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

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

Вы не можете включать статические методы в контракт интерфейса. (Хотя вы можете использовать статические методы с помощью методов расширения). Вы можете ссылаться на статические методы с помощью ссылки на делегат.

0
добавлено

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

0
добавлено

Да, делегат можно рассматривать как интерфейс с одним методом.

0
добавлено

Существует небольшая разница, делегаты могут получить доступ к переменным-членам классов, в которых они определены. В C# (в отличие от Java) все внутренние классы считаются статическими. Поэтому, если вы используете интерфейс для управления обратным вызовом, например, ActionListener для кнопки. Внутренний класс реализации должен быть передан (через конструктор) ссылками на части содержащего класса, с которыми ему может потребоваться взаимодействие во время обратного вызова. Делегаты не имеют этого ограничения, поэтому уменьшают количество кода, необходимого для реализации обратного вызова.

Более короткий, более сжатый код также является достойным преимуществом.

0
добавлено
Это не только переменные-члены. Это даже локальные переменные или параметры метода, в котором определен анонимный делегат.
добавлено автор Daniel Earwicker, источник
@supercat Исправить. Но почему вы считаете, что интерфейсы будут «работать лучше», чем делегаты? Делегат логически совпадает с интерфейсом одного метода.
добавлено автор Daniel Earwicker, источник
(1) «экземпляры кучи» не особенно дороги в CLR. См. smellegantcode.wordpress. com/2009/03/01/& hellip; (2) Что не так с delegate void MyDelegate (T v) где T: IFoo, IBar; ?
добавлено автор Daniel Earwicker, источник
Ах, ты имеешь в виду вот это? smellegantcode.wordpress .com/2010/04/17/& hellip;
добавлено автор Daniel Earwicker, источник
@DanielEarwicker: процедура, в которой определяется замыкание, определяет специальный «скрытый» класс для хранения локальных переменных, создает экземпляр такого класса всякий раз, когда эти переменные вводят область, и использует поля этого класса вместо «нормального» локального переменные. Сценарии, которые используют замыкания, будут работать лучше с интерфейсами, чем с делегатами.
добавлено автор supercat, источник
@DanielEarwicker: По крайней мере две причины: (1) Предположим, что в какой-то ситуации процедура вызовет Action , и в этой ситуации я хочу, чтобы она вызывала Foo (x) с текущим значением локальной переменной x . Передача такого запроса как делегата требует создания двух экземпляров кучи: временного объекта класса для хранения x и делегата для его вызова. Передача запроса в виде типа интерфейса потребует создания одного экземпляра кучи (реализация интерфейса). Передача его в качестве обобщенного интерфейса может не потребовать создания
добавлено автор supercat, источник
(2) Интерфейсы могут иметь открытые общие методы, тогда как делегаты не могут. Предположим, что у меня есть набор объектов, не имеющих общего базового типа, но все они имеют типы, ограниченные для реализации IFoo и IBar. Я хотел бы иметь возможность запускать внешнюю программу, которая принимает параметр, ограниченный IFoo и IBar. Невозможно указать, что делегат принимает параметр типа T: IFoo, IBar , но можно объявить метод интерфейса, который делает это.
добавлено автор supercat, источник
@DanielEarwicker: Проблема в том, что подпрограмма, которая создает делегат, должна указывать тип T . Предположим, что у класса есть набор объектов, которые все реализуют I1 и I2 , но у них нет общего базового типа, который реализует оба; он хочет выставить метод ForAll . Если такой метод принял объект типа interface IActUponConstrained {void Act (T param), где T: I1, I2;} , он мог бы вызвать Act < T> для каждого элемента без объекта, который создал IActUponConstrained , чт
добавлено автор supercat, источник
@DanielEarwicker: Если вы не хотите использовать Reflection, общий интерфейс необходим, потому что типичные параметры ввода могут перемещаться «вниз» (глубже) в стек вызовов. Коллекция абстрактного класса ThingImplementing может содержать экземпляры производных классов ThingImplementing .WithType где ActualType: I1, I2 . Каждый экземпляр привязан к родовому типу, с которым он был создан, но если другой код будет вызываться с использованием этого параметра типа, экземпляр должен будет выполнить сам вызов. Если Foo и
добавлено автор supercat, источник
... оба интерфейса, нет возможности определить делегата с параметром типа, ограниченным как I1 , так и I2 , к которому ThingImplementing .WithType может передать Foo , и к которому ThingImplementing .WithType может пройти панель . Однако можно было бы определить реализацию IActUponConstrained , передать его обоим объектам и иметь первый вызов Act (Foo param) , тогда как второй вызывает Act (Bar param) .
добавлено автор supercat, источник
@DanielEarwicker: Точно. Способность потребителя типа интерфейса поставлять общий тип параметра отличается от всего, что может быть сделано с делегатами .net. На самом деле, я не уверен, что в рамках платформы возникнет какая-то реальная потребность, если компиляторы могли бы сделать для общих интерфейсов несколько вещей, которые они выполняют для делегатов (например, с учетом интерфейса с Invoke метод, имеющий определенную подпись, построить класс, конструктор которого принимает две реализации этого интерфейса и который реализует вызов, вызывая оба экземпляра). Конечно...
добавлено автор supercat, источник
... generics не существовало в .net 1.0, поэтому квази-общие делегаты были необходимой остановкой. Кстати, интересно, почему производные от Delegate , созданные компилятором, не определяют свой собственный метод Комбинирование и Удалить ? Такие методы могут корректно работать с контравариантными типами делегатов, тогда как Delegate.Combine не может.
добавлено автор supercat, источник
Интересно отметить, что делегаты могут получить доступ к членам. Метод класса, который должен быть передан, может легко действовать как курьер для результирующих данных, но прямой доступ - это гораздо более быстрый способ сделать это.
добавлено автор Rick Minerich, источник

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

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

Более того, с появлением лямбда-выражений они теперь также могут быть легко определены на лету, что является огромным бонусом. Хотя ВОЗМОЖНО строить классы «на лету» на C#, это действительно огромная боль в прикладе.

Сравнение двух - интересное понятие. Я ранее не рассматривал, насколько похожи идеи из практики и структуры структурирования.

0
добавлено

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

Делегаты были вдохновлены (частично) из-за того, что черное искусство указателей на методе C ++ не соответствует определенным целям. Классическим примером является реализация механизма передачи сообщений или обработки событий. Делегаты позволяют вам определять подпись метода без каких-либо знаний о типах или интерфейсах класса. Я мог бы определить делегат «void eventHandler (Event * e)» и вызывать его на любом классе, который его реализовал.

Для некоторого понимания этой классической проблемы и почему делегаты желательны прочитать это и затем это .

0
добавлено