Как добавить делегата в интерфейс C #

91

Мне нужно, чтобы в моем классе было несколько делегатов.

Я хотел бы использовать интерфейс, чтобы «напомнить» мне установить этих делегатов.

Как?

Мой класс выглядит так:

public class ClsPictures : myInterface
{
    // Implementing the IProcess interface
    public event UpdateStatusEventHandler UpdateStatusText;
    public delegate void UpdateStatusEventHandler(string Status);

    public event StartedEventHandler Started;
    public delegate void StartedEventHandler();
}

Мне нужен интерфейс, чтобы заставить этих делегатов:

public interface myInterface
{
   // ?????
}
Асаф
источник

Ответы:

142

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

public delegate void UpdateStatusEventHandler(string status);
public delegate void StartedEventHandler();

public interface IMyInterface
{       
    event UpdateStatusEventHandler StatusUpdated;    
    event StartedEventHandler Started;
}

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

Джон Скит
источник
Это просто не работает для меня. Я уверен, что сделал все таким образом, но мой обработчик в моем интерфейсе не распознается. Мой класс, реализующий интерфейс, жалуется, что класс не соответствует типу, возвращаемому интерфейсом System.EventHandler.
Чаки
1
@Chucky: Похоже, вам следует задать новый вопрос с коротким, но полным примером, демонстрирующим проблему. Ответ я дал действительно делает работу.
Джон Скит
4
Я не понимал, что делегаты имеют такой же уровень доступности, что и классы. Я всегда думал, что их нужно инкапсулировать внутри класса.
Purusartha
7
@Purusartha: Это не доступность - дело в том, что делегаты являются типами (фактически, классами) в той же степени, что и интерфейсы и т. Д.
Джон Скит
29

Начиная с .NET 3.5, вы также можете использовать делегаты System.Action без необходимости объявлять собственный тип.

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

public interface myInterface
{       
   // Implementing the IProcess interface
   event Action<String> UpdateStatusText;

   event Action Started;
}
ДЕЛЮКС
источник
+1. Я считаю Action / Func гораздо более элегантным (и читаемым), чем традиционные типы делегатов, и вы можете определить их в своем интерфейсе.
chrnola
2
Этот ответ не затрагивает вопрос (как реализовать myInterface).
lharper71
10

Просто выставьте делегат как свойство

public delegate void UpdateStatusEventHandler(string status);
public delegate void StartedEventHandler();

public interface IMyInterface
{       
    UpdateStatusEventHandler StatusUpdated {get; set;}    
    StartedEventHandler Started {get; set;}
}
Корнет Григорий
источник
7

Ответ Джона Скита правильный, я просто хочу добавить примечание.

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

Если вы хотите обеспечить соблюдение некоторых стандартов кодирования в своем проекте, вы можете попробовать использовать инструменты анализа кода (например, в Visual Studio) - они позволяют расширения, которые вы можете включить, чтобы добавить свои собственные правила анализа кода.

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

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

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

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

Ваш класс conrete будет выглядеть так:

public class ClsPictures : myInterface
{
    // Implementing the IProcess interface
    public event EventHandler<UpdateStatusEventArgs> UpdateStatusText;
    //no need for this anymore: public delegate void UpdateStatusEventHandler(string Status);

    public event EventHandler<StartedEventArgs> Started;
    //no need for this anymore: public delegate void StartedEventHandler();
}

Ваш интерфейс будет выглядеть так:

public interface myInterface
{
   event EventHandler<StartedEventArgs> Started;
   event EventHandler<UpdateStatusEventArgs> UpdateStatusText;
}

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

Для справки: https://msdn.microsoft.com/en-us/library/edzehd2t(v=vs.110).aspx

Рубен Сото
источник
0

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

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

Например, используя шаблон инверсии управления:

class Form1 : Form, IForm {
   public Form1() {
     Controls.Add(new Foo(this));
   }

   // Required to be defined here.
   void IForm.Button_OnClick(object sender, EventArgs e) {
     ...
     // Cast qualifier expression to 'IForm' assuming you added a property for StatusBar.
     //((IForm) this).StatusBar.Text = $"Button clicked: ({e.RowIndex}, {e.SubItem}, {e.Model})";
   }
 }

Вы можете попробовать что-то подобное.

interface IForm {
  void Button_OnClick(object sender, EventArgs e);
}


class Foo : UserControl {
  private Button btn = new Button();

  public Foo(IForm ctx) {
     btn.Name = "MyButton";
     btn.ButtonClick += ctx.Button_OnClick;
     Controls.Add(btn);
  }
}
Задержка
источник