Cómo agregar un delegado a una interfaz C #

91

Necesito tener algunos delegados en mi clase.

Me gustaría usar la interfaz para "recordarme" que establezca estos delegados.

¿Cómo?

Mi clase se ve así:

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

Necesito una interfaz para forzar a esos delegados:

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

Respuestas:

142

Esos están declarando tipos de delegados . No pertenecen a una interfaz. Sin embargo, los eventos que usan esos tipos de delegados están bien para estar en la interfaz:

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

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

La implementación no volverá a declarar (y no debería) el tipo de delegado, como tampoco volvería a declarar cualquier otro tipo utilizado en una interfaz.

Jon Skeet
fuente
Esto simplemente no funciona para mí. Estoy seguro de que he hecho todo de esta manera y, sin embargo, no se reconoce a mi controlador dentro de mi interfaz. Mi clase que implementa la interfaz se queja de que la clase no coincide con el tipo de retorno de interfaz de System.EventHandler.
Chucky
1
@Chucky: Parece que debería hacer una nueva pregunta con un ejemplo breve pero completo que demuestre el problema. La respuesta que he dado realmente hace el trabajo.
Jon Skeet
4
No me di cuenta de que los delegados tienen el mismo nivel de accesibilidad que las clases. Siempre pensé que debían encapsularse dentro de una clase.
Purusartha
7
@Purusartha: Eso no es accesibilidad, es solo una cuestión de que los delegados sean tipos (clases, de hecho) tanto como interfaces, etc.
Jon Skeet
29

Desde .NET 3.5 también puede utilizar los delegados System.Action, sin la necesidad de declarar su propio tipo.

Esto daría como resultado la siguiente interfaz:

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

   event Action Started;
}
DELUXENIZADO
fuente
+1. Creo que Action / Func es mucho más elegante (y legible) que los tipos de delegados tradicionales y puede definirlos en su interfaz.
chrnola
2
Esta respuesta no aborda la pregunta (cómo implementar myInterface).
lharper71
10

Simplemente exponga al delegado como una propiedad

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

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

La respuesta de Jon Skeet es correcta, solo quiero agregar una nota.

Las interfaces no están ahí para "recordarte" qué hacer o qué incluir en tus clases. Las interfaces son medios de abstracción que se utilizan en los métodos de diseño y programación orientados a objetos. Tal vez no necesite una declaración de interfaz en absoluto, a menos que desee ver algunas instancias de clase concretas como interfaz en otra parte de su programa (Abstracción).

Si desea hacer cumplir algunos estándares de codificación en su proyecto, puede intentar usar herramientas de análisis de código (como en Visual Studio): permiten extensiones que puede incorporar para agregar sus propias reglas de análisis de código.

Usando el análisis de código, si "olvida" agregar los delegados (aunque no veo el sentido de olvidarlo, ya que si el delegado no se usa, no es necesario) obtendrá una advertencia / error.

Iravanchi
fuente
1
Puede que tengas razón, pero hay ocasiones en las que haces suposiciones de que incluso con una buena documentación es difícil de recordar después de un tiempo. Intento reutilizar mi código, pero a veces no puedo recordar cuál es el núcleo y qué no (cuando se trata de delegados, es difícil para mí ver el panorama general ...)
Asaf
1

Uno de sus comentarios hizo referencia al tipo de retorno del controlador de eventos. ¿Está más preocupado por el tipo de controlador o por los datos que provienen del evento? Si es lo último, entonces esto puede ayudar. De lo contrario, esta solución no será suficiente, pero puede ayudarlo a acercarse a lo que está buscando.

Todo lo que tiene que hacer es declarar sus controladores de eventos como controladores de eventos genéricos tanto en la interfaz como en su implementación y puede personalizar los resultados devueltos.

Tu clase de conrete se vería así:

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

Su interfaz se vería así:

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

Ahora que los argumentos de evento están devolviendo sus tipos, puede conectarlos en cualquier controlador que defina.

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

Reuben Soto
fuente
0

La interfaz heredada dentro de su clase derivada le recordará que defina y vincule las cosas que declaró en ella.

Pero es posible que también desee usarlo explícitamente y aún deberá asociarlo a un objeto.

Por ejemplo, usando un patrón de inversión de control:

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

Puedes probar algo como esto.

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);
  }
}
Latencia
fuente