Что использовать? делегат, событие или Func <T>? - PullRequest
7 голосов
/ 09 апреля 2011

Я хочу предоставить объектам внутри библиотеки классов возможность «выводить» сообщения, не беспокоясь о том, как они выводятся. Библиотека классов может использоваться в консольном приложении, приложении Windows WinForm или WPF или на веб-странице.

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

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

Интерфейс:

namespace Test
{
  using System;
  using System.Xml.Linq;

  public interface IAction
  {
    DisplayMessageDelegate DisplayMessage(string message);
    void Execute();
    XElement Serialize(XName elementName);
  }

  public delegate void DisplayMessageDelegate(string message);
}

Оттуда я не уверен, как реализовать это поведение: (Кстати, я знаю, что этот код не будет компилироваться ...)

public class ActionClass1 : IAction
{
  // Other methods not shown...
  void Execute()
  {
    if (this.DisplayMessage != null)
    {
      this.DisplayMessage(“Hello”);
    }
  }
}

public class ConsoleClass
{
  ActionClass1 class1 = new ActionClass1();
  class1.DisplayMessage = { x =>  Console.WriteLine(x); };
}

public class WinFormClass
{
  ActionClass1 class1 = new ActionClass1();
  Class1.DisplayMessage = {x => DisplayTextBox.Text = x; };
}

Ответы [ 6 ]

12 голосов
/ 09 апреля 2011

Если вы хотите, чтобы возможность подключить несколько делегатов для ответа на один вызов Execute, я бы определенно использовал event.Если вы хотите, чтобы было подключено только одно действие, используйте делегат Action или Func.

. Например, один из делегатов Action должен работать.В вашем случае это будет Action<string>, поскольку ваш делегат принимает строковый аргумент.Action - это просто делегат, который принимает ноль или более аргументов и возвращает void.Похоже, что вы ничего не возвращаете, поэтому я предлагаю Action.

Вы хотите использовать Func<TResult>, только если ваш делегат должен что-то вернуть.Разница между Func и Action заключается в том, что делегаты Func имеют тип возврата, а делегаты Action - нет.Любой из этих делегатов имеет общие версии, которые могут принимать до 16 или около того аргументов.

Если вам нужно более 16 аргументов для делегата, вы можете пересмотреть дизайн:)

6 голосов
/ 09 апреля 2011

Вы можете сделать это, используя Action<string>.

Вы не хотели бы использовать Func<T>, так как это определение делегата, который не принимает аргументов, но возвращает одно значение типа T. Action<T>, с другой стороны, является делегатом, который принимает один аргумент тип T.

Я бы предложил попробовать:

public interface IAction
{
    Action<string> DisplayMessage { get; set; }

    void Execute();
    XElement Serialize(XName elementName);
}

Как только вы реализовали этот интерфейс (полностью), вы можете использовать его через:

public class ConsoleClass
{
    public void SomeMethod()
    {
        ActionClass1 class1 = new ActionClass1();
        class1.DisplayMessage = x => Console.WriteLine(x);
    }
}

Или:

public class ConsoleClass
{
    public void SomeMethod()
    {
        ActionClass1 class1 = new ActionClass1();
        class1.DisplayMessage = this.Print;
    }

    private void Print(string message)
    {
        Console.WriteLine(message);
    }
}

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

1 голос
/ 09 апреля 2011

Здесь

DisplayMessageDelegate DisplayMessage(string message);

Вы описываете метод, который принимает строку и возвращает DisplayMessageDelegate. Используйте

 event DisplayMessageDelegate DisplayMessage;

вместо.

1 голос
/ 09 апреля 2011

Ваше определение интерфейса неверно.Вам нужно будет указать его так:

namespace Test
{
  using System;
  using System.Xml.Linq;

  public interface IAction
  {
    DisplayMessageDelegate DisplayMessage { get; set; };
    void Execute();
    XElement Serialize(XName elementName);
  }

  public delegate void DisplayMessageDelegate(string message);
}

и затем реализовать интерфейс.

0 голосов
/ 09 апреля 2011

Вместо:

DisplayMessageDelegate DisplayMessage(string message);

Do:

event Action<string> DisplayMessage;

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

0 голосов
/ 09 апреля 2011

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

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