Делегаты против интерфейсов в C # - PullRequest
50 голосов
/ 01 января 2012

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

Я знаю, что делегаты служат указателями на функции, используемые в C ++. Дело в том, что в C # они служат главным образом альтернативой интерфейсам и полиморфизму. Так как я могу создавать подклассы определенного класса и предоставлять им соответствующие методы для каждого, что предлагают делегаты в дополнение к этому? Существуют ли случаи, которые предусматривают их использование, или улучшается удобство сопровождения кода при использовании делегатов? Вы бы порекомендовали их широкое распространение через интерфейсы?

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

Ответы [ 5 ]

70 голосов
/ 01 января 2012

Да, делегаты во многом похожи на интерфейсы с одним методом. Тем не менее:

  • В CLR встроена поддержка для них
  • В них есть поддержка, в том числе многоадресные способности и асинхронный вызов
  • Существует дополнительная поддержка языка C # / VB в форме преобразования групп методов, лямбда-выражений, анонимных методов
  • Они предназначены для событий (то есть события и делегаты являются своего рода парой совпадений)
  • Они означают, что не нужно реализовывать интерфейс в отдельном классе для каждого экземпляра делегата, который вы хотите создать.

Последний пункт является наиболее важным - рассмотрим выражение LINQ:

var query = collection.Where(x => x > 5)
                      .Select(x => x * x);

А теперь представьте, что если выразить логику x > 5 и x * x, вам нужно было написать отдельный класс для каждого выражения и реализовать интерфейс: количество кода и полезного кода было бы нелепым. Теперь, конечно, язык может быть спроектирован так, чтобы разрешать преобразования из лямбда-выражений в реализации интерфейса через отдельные классы, но тогда вы все равно потеряете возможность просто написать отдельный метод и создать делегат с этим в качестве цели. Вы также все равно потеряете способности мульти-каста.

В качестве аналогичного упражнения для мышления рассмотрим циклические операторы, такие как while и for. Нужно ли нам , когда у нас есть goto? Нету. Но жизнь намного лучше с им. То же самое относится и к делегатам - и действительно, к свойствам, событиям и т. Д. Все они упрощают разработку.

28 голосов
/ 01 января 2012

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

void delegate XYZ(int p);

interface IXyz {
    void doit(int p);
}

class One {
    // All four methods below can be used to implement the XYZ delegate
    void XYZ1(int p) {...}
    void XYZ2(int p) {...}
    void XYZ3(int p) {...}
    void XYZ4(int p) {...}
}

class Two : IXyz {
    public void doit(int p) {
        // Only this method could be used to call an implementation through an interface
    }
}
16 голосов
/ 01 января 2012

С Когда использовать делегаты вместо интерфейсов (MSDN) :

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

Используйте делегата в следующих случаях:

  • Используется шаблон проектирования событий.
  • Желательно инкапсулировать статический метод.
  • У вызывающей стороны нет необходимости обращаться к другим свойствам, методам или интерфейсам объекта, реализующего метод.
  • Требуется легкая композиция.
  • Классу может потребоваться более одной реализации метода.

Используйте интерфейс в следующих случаях:

  • Существует группа связанных методов, которые могут быть вызваны.
  • Классу нужна только одна реализация метода.
  • Класс, использующий интерфейс, захочет привести этот интерфейс к другим интерфейсам или типам классов.
  • Реализуемый метод связан с типом или идентификатором класса: например, методы сравнения.

Одним хорошим примером использования интерфейса с одним методом вместо делегата является IComparable или общая версия IComparable (Of T). IComparable объявляет метод CompareTo, который возвращает целое число, которое указывает отношение меньше, равно или больше, чем между двумя объектами одного типа. IComparable может использоваться как основа алгоритма сортировки. Хотя использование метода сравнения делегатов в качестве основы алгоритма сортировки было бы правильным, оно не является идеальным. Поскольку возможность сравнения принадлежит классу, а алгоритм сравнения не изменяется во время выполнения, идеальным является интерфейс с одним методом.

С Делегаты против интерфейсов в C # :

Делегаты и интерфейсы - это две разные концепции в C #, но они имеют общую общность. И делегаты, и интерфейсы включают только объявление. Реализация выполняется другим объектом программирования.

4 голосов
/ 01 января 2012

Делегаты и интерфейсы - это две разные концепции в C #.

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

1 голос
/ 03 января 2012

Единственными реальными преимуществами делегатов над интерфейсами являются

  1. Делегаты могут иметь дело с различными типами аргументов даже до того, как .Net поддерживает обобщенные типы.
  2. Класс может создавать делегатов, представляющих несколько различных методов, совместно использующих одну и ту же сигнатуру;замена делегатов интерфейсами будет означать, что каждый класс может предоставлять один метод, который реализует каждый интерфейс в стиле делегата без создания вспомогательных классов, но для каждой дополнительной реализации потребуется вспомогательный класс.

Когда .net не поддерживает обобщенные элементы, делегаты были существенной частью этого, так как объявление отдельных неуниверсальных интерфейсов для каждой подписи каждой функции, которую нужно передать, было бы неработоспособным.Если бы .net поддерживал дженерики с самого начала, делегаты не были бы необходимы, за исключением определенных сценариев, включающих Reflection, и даже там, возможно, было бы наиболее полезно иметь тип Action<T,U>, являющийся реализацией IAction<T,U> (чтобыкод, которому просто нужно что-то, что он может Invoke будет использовать интерфейс).Подход на основе интерфейса потребовал бы создания классов с одним методом в тех случаях, когда классам необходимо создавать делегаты, представляющие несколько методов, но исключил бы необходимость создания отдельных экземпляров делегатов во многих распространенных случаях, когда число методовзаданная подпись должна быть точно одна.

Кстати, замена делегатов интерфейсами никоим образом не помешает созданию универсального метода Combine.Действительно, ковариация интерфейса может заставить такой метод работать лучше, чем существующий Delegate.Combine во многих отношениях.Реализация метода, аналогичного Delegate.Remove, была бы в лучшем случае неуклюжей и раздражающей, но я не могу думать ни о какой другой ситуации, кроме управления подпиской на события, которая потребовала бы использования Delegate.Remove, и подписка на события в любом случае могла бы лучше всего выполняться с использованием других подходов.

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