Como adicionar um delegado a uma interface C #

89

Eu preciso ter alguns delegados em minha classe.

Eu gostaria de usar a interface para me "lembrar" de definir esses delegados.

Como?

Minha classe é assim:

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();
}

Preciso de uma interface para forçar esses delegados:

public interface myInterface
{
   // ?????
}
Asaf
fonte

Respostas:

140

Esses são tipos de delegado declarantes . Eles não pertencem a uma interface. Os eventos que usam esses tipos de delegado podem estar na interface, embora:

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

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

A implementação não redeclarará (e não deve) redeclarar o tipo de delegado, assim como não redeclararia qualquer outro tipo usado em uma interface.

Jon Skeet
fonte
Isso simplesmente não está funcionando para mim. Tenho certeza de que fiz tudo dessa maneira e, ainda assim, meu manipulador em minha interface não está sendo reconhecido. Minha classe que implementa a interface está reclamando que a classe não corresponde ao tipo de retorno da interface de System.EventHandler.
Chucky
1
@ Chucky: Parece que você deveria fazer uma nova pergunta com um exemplo curto, mas completo, que demonstrasse o problema. A resposta que eu dei realmente faz o trabalho.
Jon Skeet
4
Não percebi que os delegados têm o mesmo nível de acessibilidade que as classes. Sempre achei que eles precisavam ser encapsulados em uma classe.
Purusartha
7
@Purusartha: Isso não é acessibilidade - é apenas uma questão de delegados serem tipos (classes, na verdade) tanto quanto interfaces etc.
Jon Skeet
29

Desde o .NET 3.5, você também pode usar os delegados System.Action, sem a necessidade de declarar seu próprio tipo.

Isso resultaria na seguinte interface:

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

   event Action Started;
}
DELUXEnized
fonte
+1. Acho que Action / Func é muito mais elegante (e legível) do que os tipos tradicionais de delegado e você pode defini-los em sua interface.
chrnola,
2
Esta resposta não aborda a questão (como implementar myInterface).
lharper71
10

Apenas exponha o delegado como uma propriedade

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

public interface IMyInterface
{       
    UpdateStatusEventHandler StatusUpdated {get; set;}    
    StartedEventHandler Started {get; set;}
}
Cornet Gregory
fonte
7

A resposta de Jon Skeet está certa, eu só quero adicionar uma nota.

As interfaces não existem para "lembrar" o que fazer ou o que incluir nas aulas. Interfaces são meios de abstração, usados ​​em programação orientada a objetos e métodos de design. Talvez você não precise de uma declaração de interface, a menos que queira ver algumas instâncias de classe concretas como a interface em outro lugar em seu programa (Abstração).

Se você deseja impor alguns padrões de codificação em seu projeto, pode tentar usar ferramentas de análise de código (como no Visual Studio) - Elas permitem extensões, que você pode incorporar para adicionar suas próprias regras de análise de código.

Usando a análise de código, se você "esquecer" de adicionar os delegados (embora eu não veja por que esquecê-lo, como se o delegado não fosse usado, não é necessário), você receberá um aviso / erro.

Iravanchi
fonte
1
Você pode estar certo, mas às vezes você faz algumas suposições de que, mesmo com uma boa documentação, é difícil lembrar depois de um tempo. Tento reutilizar meu código, mas às vezes não consigo me lembrar qual é o núcleo e o que não é (quando se trata de delegados - é difícil para mim ver o quadro geral ...)
ASAF
1

Um de seus comentários fez referência ao tipo de retorno do manipulador de eventos. Você está mais preocupado com o tipo de manipulador ou com os dados que retornam do evento? Se for o último, isso pode ajudar. Caso contrário, esta solução não será suficiente, mas pode ajudar a chegar mais perto do que você está procurando.

Tudo o que você precisa fazer é declarar seus manipuladores de eventos como manipuladores de eventos genéricos na interface e em sua implementação e você pode personalizar os resultados de retorno.

Sua classe de conteúdo ficaria assim:

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();
}

Sua interface ficaria assim:

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

Agora que os argumentos de evento estão retornando seus tipos, você pode conectá-los a qualquer manipulador que definir.

Para referência: https://msdn.microsoft.com/en-us/library/edzehd2t(v=vs.110).aspx

Reuben Soto
fonte
0

A interface herdada em sua classe derivada o lembrará de definir e vincular as coisas que você declarou nela.

Mas você também pode querer usá-lo explicitamente e ainda precisará associá-lo a um objeto.

Por exemplo, usando um padrão de Inversão de Controle:

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})";
   }
 }

Você pode tentar algo assim.

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);
  }
}
Latência
fonte