Что они подразумевают под образцом событийного дизайна?
Скорее всего, они ссылаются на реализацию шаблона наблюдателя, который является базовой языковой конструкцией в C #, представленной как « события ». Прослушивание событий возможно, подключив к ним делегата. Как отметил Ям Маркович, EventHandler
это обычный базовый тип делегата для событий, но может использоваться любой тип делегата.
Насколько легко получается состав, если используется делегат?
Это, вероятно, относится только к гибкости, которую предлагают делегаты. Вы можете легко «составить» определенное поведение. С помощью лямбды синтаксис для этого также очень лаконичен. Рассмотрим следующий пример.
class Bunny
{
Func<bool> _canHop;
public Bunny( Func<bool> canHop )
{
_canHop = canHop;
}
public void Hop()
{
if ( _canHop() ) Console.WriteLine( "Hop!" );
}
}
Bunny captiveBunny = new Bunny( () => IsBunnyReleased );
Bunny lazyBunny = new Bunny( () => !IsLazyDay );
Bunny captiveLazyBunny = new Bunny( () => IsBunnyReleased && !IsLazyDay );
Чтобы сделать что-то похожее с интерфейсами, вам потребуется либо использовать шаблон стратегии, либо использовать (абстрактный) базовый Bunny
класс, из которого вы расширяете более специфические кролики.
если есть группа связанных методов, которые могут быть вызваны, то используйте интерфейс - какое это имеет преимущество?
Опять же, я буду использовать кроликов, чтобы продемонстрировать, как это будет проще.
interface IAnimal
{
void Jump();
void Eat();
void Poo();
}
class Bunny : IAnimal { ... }
class Chick : IAnimal { ... }
// Using the interface.
IAnimal bunny = new Bunny();
bunny.Jump(); bunny.Eat(); bunny.Poo();
IAnimal chick = new Chick();
chick.Jump(); chick.Eat(); chick.Poo();
// Without the interface.
Action bunnyJump = () => bunny.Jump();
Action bunnyEat = () => bunny.Eat();
Action bunnyPoo = () => bunny.Poo();
bunnyJump(); bunnyEat(); bunnyPoo();
Action chickJump = () => chick.Jump();
Action chickEat = () => chick.Eat();
...
если классу нужна только одна реализация метода, используйте интерфейс - как это оправдано с точки зрения преимуществ?
Для этого рассмотрим первый пример с кроликом еще раз. Если когда-либо требуется только одна реализация - никакой пользовательской композиции не требуется - вы можете представить это поведение как интерфейс. Вам никогда не придется создавать лямбды, вы можете просто использовать интерфейс.
Вывод
Делегаты предлагают гораздо больше гибкости, а интерфейсы помогают вам заключать надежные контракты. Поэтому я нахожу последний упомянутый пункт: «Классу может потребоваться более одной реализации метода». безусловно, самый актуальный.
Дополнительная причина, когда использовать делегаты, - это когда вы хотите выставить только часть класса, из которой вы не можете настроить исходный файл.
В качестве примера такого сценария (максимальная гибкость, нет необходимости изменять источники) рассмотрим эту реализацию алгоритма бинарного поиска для любой возможной коллекции, просто передав двух делегатов.
1) Шаблоны событий, ну что ж, классика - это шаблон Observer, вот хорошая ссылка Microsoft
Статья, на которую вы ссылаетесь, не очень хорошо написана, и делает вещи более сложными, чем необходимо. Статический бизнес - это здравый смысл, вы не можете определить интерфейс со статическими членами, поэтому, если вы хотите полиморфное поведение статического метода, вы должны использовать делегат.
Ссылка выше говорит о делегатах и о том, почему они хороши для композиции, что может помочь с вашим запросом.
источник
Прежде всего, хороший вопрос. Я восхищаюсь вашим вниманием к полезности, а не слепо принимая «лучшие практики». +1 за это.
Я читал это руководство раньше. Вы должны вспомнить кое-что об этом - это просто руководство, в основном для новичков в C #, которые знают, как программировать, но не очень хорошо знакомы с тем, как C # делает что-то. Это не столько страница правил, сколько страница, которая описывает, как все обычно делается. И так как они уже сделали это повсюду, было бы неплохо оставаться последовательным.
Я доберусь до сути, отвечая на ваши вопросы.
Прежде всего, я предполагаю, что вы уже знаете, что такое интерфейс. Что касается делегата, достаточно сказать, что это структура, содержащая типизированный указатель на метод вместе с необязательным указателем на объект, представляющий
this
аргумент для этого метода. В случае статических методов последний указатель является нулевым.Есть также многоадресные делегаты, которые похожи на делегаты, но им может быть назначено несколько таких структур (то есть один вызов Invoke для многоадресного делегата вызывает все методы в его назначенном списке вызовов).
Что они подразумевают под образцом событийного дизайна?
Они подразумевают использование событий в C # (в котором есть специальные ключевые слова для сложной реализации этого чрезвычайно полезного шаблона). События в C # инициируются многоадресными делегатами.
Когда вы определяете событие, например, в этом примере:
Компилятор фактически преобразует это в следующий код:
Затем вы подписываетесь на событие, выполнив
Который сводится к
Так что это происходит в C # (или .NET в целом).
Насколько легко получается состав, если используется делегат?
Это может быть легко продемонстрировано:
Предположим, у вас есть класс, который зависит от набора действий, которые ему нужно передать. Вы можете инкапсулировать эти действия в интерфейсе:
И любой, кто хочет передать действия вашему классу, должен сначала реализовать этот интерфейс. Или вы можете сделать их жизнь проще , полагаясь на следующий класс:
Таким образом, вызывающие пользователи должны только создать экземпляр RequiredMethods и привязать методы к делегатам во время выполнения. Это является , как правило , легче.
Такой способ ведения дел чрезвычайно полезен при правильных обстоятельствах. Подумайте об этом - зачем зависеть от интерфейса, когда все, что вас действительно волнует, это передача вам реализации?
Преимущества использования интерфейсов при наличии группы связанных методов
Полезно использовать интерфейсы, потому что интерфейсы обычно требуют явных реализаций во время компиляции. Это означает, что вы создаете новый класс.
И если у вас есть группа связанных методов в одном пакете, полезно, чтобы этот пакет был повторно использован другими частями кода. Так что, если они могут просто создать экземпляр класса вместо создания набора делегатов, это проще.
Преимущества использования интерфейсов, если классу нужна только одна реализация
Как отмечалось ранее, интерфейсы реализуются во время компиляции - это означает, что они более эффективны, чем вызов делегата (который сам по себе является уровнем косвенности).
«Одна реализация» может означать реализацию, которая существует в одном четко определенном месте.
В противном случае реализация может происходить из любой точки программы, которая просто соответствует сигнатуре метода. Это обеспечивает большую гибкость, поскольку методы должны соответствовать только ожидаемой сигнатуре, а не принадлежать к классу, который явно реализует определенный интерфейс. Но такая гибкость может обойтись дорого и фактически нарушает принцип подстановки Лискова , потому что в большинстве случаев вам нужна четкость, потому что она сводит к минимуму вероятность несчастных случаев. Так же, как статическая печать.
Термин может также относиться к многоадресным делегатам здесь. Методы, объявленные интерфейсами, могут быть реализованы только один раз в классе реализации. Но делегаты могут накапливать несколько методов, которые будут вызываться последовательно.
В общем, похоже, что руководство не достаточно информативно, а просто функционирует так, как оно есть - руководство, а не свод правил. Некоторые советы могут показаться немного противоречивыми. Вам решать, когда будет правильно применять что. Руководство, кажется, только дает нам общий путь.
Я надеюсь, что ваши вопросы были удовлетворены. И снова, спасибо за вопрос.
источник
Если моя память о .NET все еще сохраняется, делегат - это в основном функция ptr или функтор. Он добавляет слой косвенности к вызову функции, так что функции могут быть заменены без необходимости изменения вызывающего кода. Это то же самое, что делает интерфейс, за исключением того, что интерфейс упаковывает несколько функций вместе, и разработчик должен реализовать их вместе.
Вообще говоря, шаблон событий - это тот, где что-то реагирует на события, происходящие в другом месте (например, сообщения Windows). Множество событий обычно имеет открытый конец, они могут приходить в любом порядке и не обязательно связаны друг с другом. Для этого хорошо работают делегаты, поскольку каждое событие может вызывать одну функцию без ссылки на серию реализующих объектов, которые также могут содержать множество не относящихся к делу функций. Кроме того (здесь моя память .NET расплывчата), я думаю, что к событию можно присоединить несколько делегатов.
Составление, хотя я не очень знаком с этим термином, в основном проектирует один объект, имеющий несколько частей, или агрегированных дочерних элементов, которым передается работа. Делегаты позволяют детям смешиваться и подбираться более специальным образом, когда интерфейс может быть излишним или вызывать слишком сильную связь, а также жесткость и хрупкость, которые с ним связаны.
Преимущество интерфейса для связанных методов состоит в том, что методы могут совместно использовать состояние реализующего объекта. Функции делегата не могут так четко разделять или даже содержать состояние.
Если классу нужна отдельная реализация, интерфейс является более подходящим, поскольку в любом классе, реализующем всю коллекцию, может быть выполнена только одна реализация, и вы получаете преимущества от реализующего класса (состояние, инкапсуляция и т. Д.). Если реализация может измениться из-за состояния времени выполнения, делегаты работают лучше, потому что их можно заменить на другие реализации, не влияя на другие методы. Например, если есть три делегата, каждый из которых имеет две возможные реализации, вам потребуется восемь различных классов, реализующих интерфейс с тремя методами, чтобы учесть все возможные комбинации состояний.
источник
Шаблон разработки «события» (более известный как шаблон наблюдателя) позволяет присоединять к делегату несколько методов одной и той же подписи. Вы не можете сделать это с помощью интерфейса.
Я совсем не уверен, что состав для делегата легче, чем интерфейс. Это очень странное утверждение. Интересно, если он имеет в виду, потому что вы можете прикрепить анонимные методы к делегату.
источник
Самое большое разъяснение, которое я могу предложить:
Так:
источник
Ваш вопрос о событиях уже хорошо освещен. И также верно, что интерфейс может определять несколько методов (но на самом деле не нужно), в то время как тип функции только когда-либо накладывает ограничения на отдельную функцию.
Однако реальная разница заключается в следующем:
Конечно, но что это значит?
Давайте рассмотрим этот пример (код написан на haXe, поскольку мой C # не очень хорош):
Теперь метод filter позволяет легко передавать только небольшой фрагмент логики, который не знает о внутренней организации коллекции, в то время как коллекция не зависит от логики, данной ему. Отлично. За исключением одной проблемы:
Коллекция действительно зависит от логики. Коллекция по своей сути предполагает, что переданная функция предназначена для проверки значения в соответствии с условием и возврата результата теста. Обратите внимание, что не все функции, которые принимают одно значение в качестве аргумента и возвращают логическое значение, на самом деле являются простыми предикатами. Например, метод удаления нашей коллекции является такой функцией.
Предположим, мы позвонили
c.filter(c.remove)
. В результате будет коллекцией со всеми элементамиc
времениc
сам становится пустым. Это прискорбно, потому что, естественно, мы ожидаем, чтоc
он останется неизменным.Пример очень сконструирован. Но ключевая проблема заключается в том, что код, вызывающий
c.filter
какое-либо значение функции в качестве аргумента, не может знать, подходит ли этот аргумент (т.е. в конечном итоге сохранит инвариант). Код, создавший значение функции, может знать или не знать, что оно будет интерпретироваться как предикат.Теперь давайте изменим вещи:
Что изменилось? Что изменилось, так это то, что независимо от того, какое значение сейчас придается
filter
, явным образом подписан договор о предикате. Конечно, злые или чрезвычайно глупые программисты создают реализации интерфейса, которые не свободны от побочных эффектов и, следовательно, не являются предикатами.Но что больше не может произойти, так это то, что кто-то связывает логическую единицу данных / кода, которая по ошибке интерпретируется как предикат из-за его внешней структуры.
Итак, перефразирую сказанное выше в нескольких словах:
Преимущество явных отношений в том, что вы можете быть уверены в них. Недостаток в том, что они требуют накладных расходов на явность. И наоборот, недостаток неявных отношений (в нашем случае подпись одной функции, совпадающая с желаемой подписью) заключается в том, что вы не можете быть на 100% уверены, что можете использовать вещи таким образом. Преимущество в том, что вы можете установить отношения без всех накладных расходов. Вы можете просто быстро собрать вещи вместе, потому что их структура это позволяет. Вот что значит легкая композиция.
Это немного похоже на LEGO: вы можете просто подключить фигуру LEGO звездных войн к пиратскому кораблю LEGO, просто потому, что внешняя структура это позволяет. Теперь вы можете чувствовать, что это ужасно неправильно, или это может быть именно то, что вы хотите. Никто не собирается останавливать тебя.
источник
int IList.Count { get { ... } }
), либо неявно (public int Count { get { ... } }
). Это различие не имеет отношения к этой дискуссии, но оно заслуживает упоминания, чтобы не сбить с толку читателей.источник