Преимущества использования делегатов? - PullRequest
7 голосов
/ 19 ноября 2008

Я хочу реализовать шаблон Observer в VB.NET или C # или каком-либо другом первоклассном языке .NET. Я слышал, что для этого могут использоваться делегаты, но я не могу понять, почему они предпочтительнее, чем простые старые интерфейсы, реализованные на наблюдателях. Таким образом,

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

Ответы [ 14 ]

27 голосов
/ 28 июня 2010

Когда вы можете напрямую вызывать метод, вам не нужен делегат.

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

Вот (очень глупый) пример кода:

enum TaskStatus
{
   Started,
   StillProcessing,
   Finished
}

delegate void CallbackDelegate(Task t, TaskStatus status);

class Task
{
    public void Start(CallbackDelegate callback)
    {
        callback(this, TaskStatus.Started);

        // calculate PI to 1 billion digits
        for (...)
        {
            callback(this, TaskStatus.StillProcessing);
        }

        callback(this, TaskStatus.Finished);
    }
}

class Program
{
    static void Main(string[] args)
    {
        Task t = new Task();
        t.Start(new CallbackDelegate(MyCallbackMethod));
    }

    static void MyCallbackMethod(Task t, TaskStatus status)
    {
        Console.WriteLine("The task status is {0}", status);
    }
}

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

22 голосов
/ 28 июня 2010

Вы O / S, а я приложение. Я хочу сказать вам вызвать один из моих методов, когда вы обнаружите, что что-то происходит. Для этого я передаю вам делегата в мой метод, который я хочу, чтобы вы вызвали. Я сам не называю этот метод, потому что я хочу, чтобы вы назвали его, когда обнаружите что-то. Вы не вызываете мой метод напрямую, потому что вы не знаете (во время компиляции), что метод существует (я даже не был написан, когда вы создавали); вместо этого вы вызываете тот метод, который указан делегатом, который вы получаете во время выполнения.

9 голосов
/ 28 июня 2010

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

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

6 голосов
/ 28 июня 2010

Ты не думаешь как программист.

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

Знаменитый афоризм Дэвида Уилера выходит: все проблемы в информатике может быть решен с помощью другого уровня Косвенная.

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

4 голосов
/ 19 ноября 2008

Есть два места, где вы можете использовать делегатов в шаблоне Observer. Поскольку я не уверен, на кого вы ссылаетесь, я постараюсь ответить на оба вопроса.

Первый - использовать делегатов в теме вместо списка IObservers. Этот подход кажется более понятным при обработке многоадресной рассылки, поскольку у вас есть

private delegate void UpdateHandler(string message);
private UpdateHandler Update;

public void Register(IObserver observer)
{
    Update+=observer.Update;
}

public void Unregister(IObserver observer)
{
    Update-=observer.Update;
}

public void Notify(string message)
{
    Update(message);
}

вместо

public Subject()
{
    observers = new List<IObserver>();
}

public void Register(IObserver observer)
{
    observers.Add(observer);
}

public void Unregister(IObserver observer)
{
    observers.Remove(observer);
}

public void Notify(string message)
{
    // call update method for every observer
    foreach (IObserver observer in observers)
    {
        observer.Update(message);
    }
}

Если вам не нужно делать что-то особенное и требовать ссылки на весь объект IObserver, я бы подумал, что делегаты будут чище.

Второй случай - использовать делегаты прохода вместо IObervers, например

public delegate void UpdateHandler(string message);
private UpdateHandler Update;

public void Register(UpdateHandler observerRoutine)
{
    Update+=observerRoutine;
}

public void Unregister(UpdateHandler observerRoutine)
{
    Update-=observerRoutine;
}

public void Notify(string message)
{
    Update(message);
}

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

2 голосов
/ 28 июня 2010

Делегаты строгой типизации для интерфейсов функций / методов.

Если ваш язык занимает позицию, согласно которой должна быть строгая типизация и что он имеет первоклассные функции (обе из которых есть в C #), то он будет несовместим с , а не с делегатами. *

Рассмотрим любой метод, который принимает делегат. Если бы у вас не было делегата, как бы вы передали ему что-нибудь? И как вызываемый абонент имеет какие-либо гарантии относительно своего типа?

2 голосов
/ 28 июня 2010

Я повторяю ответ, который дал на этот вопрос .

Мне всегда нравилась метафора радиостанции.

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

Без этого механизма регистрации (или события). Радиостанция должна будет связаться со всеми радиостанциями по очереди и спросить, хочет ли она трансляции, если ваше радио ответило «да», то отправьте сигнал на нее напрямую.

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

2 голосов
/ 19 ноября 2008

Делегат, по сути, передает ссылку на метод, а не на объект ... Интерфейс - это ссылка на подмножество методов, реализуемых объектом ...

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

Если, otoh, в каком-либо методе или компоненте все, что вам нужно, это один из нескольких методов, которые могут быть в любом количестве разных классов, но все имеют одну и ту же сигнатуру, тогда вам нужно использовать делегат. *

1 голос
/ 28 июня 2010

На самом деле, между Sun и Microsoft был интересный вопрос о делегатах. В то время как Sun выступила с довольно сильной позицией в отношении делегатов, я чувствую, что Microsoft сделала еще более сильный аргумент в пользу использования делегатов. Вот сообщения:

http://java.sun.com/docs/white/delegates.html

http://msdn.microsoft.com/en-us/vjsharp/bb188664.aspx

Я думаю, вы найдете это интересное чтение ...

1 голос
/ 19 ноября 2008

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

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

Не изобретайте велосипед (если текущая версия не сломана).

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