Comment ajouter un délégué à une interface C #

89

J'ai besoin d'avoir des délégués dans ma classe.

Je voudrais utiliser l'interface pour me "rappeler" de définir ces délégués.

Comment?

Ma classe ressemble à ceci:

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

J'ai besoin d'une interface pour forcer ces délégués:

public interface myInterface
{
   // ?????
}
Asaf
la source

Réponses:

140

Ceux-ci déclarent des types de délégués . Ils n'appartiennent pas à une interface. Les événements utilisant ces types de délégués peuvent cependant être dans l'interface:

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

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

L'implémentation ne redéclarera pas (et ne devrait pas) le type de délégué, pas plus qu'elle ne redéclarerait tout autre type utilisé dans une interface.

Jon Skeet
la source
Cela ne fonctionne tout simplement pas pour moi. Je suis sûr que j'ai tout fait de cette façon, et pourtant, mon gestionnaire dans mon interface n'est pas reconnu. Ma classe qui implémente l'interface se plaint que la classe ne correspond pas au type de retour d'interface de System.EventHandler.
Chucky
1
@Chucky: On dirait que vous devriez poser une nouvelle question avec un exemple court mais complet de démonstration du problème. La réponse que je lui ai donné vraiment fait le travail.
Jon Skeet
4
Je ne savais pas que les délégués avaient le même niveau d'accessibilité que les classes. J'ai toujours pensé qu'ils devaient être encapsulés dans une classe.
Purusartha
7
@Purusartha: Ce n'est pas l'accessibilité - c'est juste une question de délégués étant des types (classes, en fait) tout autant que des interfaces, etc.
Jon Skeet
29

Depuis .NET 3.5, vous pouvez également utiliser les délégués System.Action, sans avoir à déclarer votre propre type.

Cela aboutirait à l'interface suivante:

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

   event Action Started;
}
DELUXEnized
la source
+1. Je trouve Action / Func beaucoup plus élégant (et lisible) que les types de délégués traditionnels et vous pouvez les définir dans votre interface.
chrnola
2
Cette réponse ne répond pas à la question (comment implémenter myInterface).
lharper71
10

Exposez simplement le délégué en tant que propriété

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

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

La réponse de Jon Skeet est juste, je veux juste ajouter une note.

Les interfaces ne sont pas là pour vous "rappeler" ce que vous devez faire ou ce que vous devez inclure dans vos cours. Les interfaces sont des moyens d'abstraction, utilisés dans les méthodes de programmation et de conception orientées objet. Peut-être que vous n'avez pas du tout besoin d'une déclaration d'interface, à moins que vous ne souhaitiez voir des instances de classe concrètes comme interface ailleurs dans votre programme (Abstraction).

Si vous souhaitez appliquer certaines normes de codage dans votre projet, vous pouvez essayer d'utiliser des outils d'analyse de code (comme dans Visual Studio) - Ils autorisent les extensions, que vous pouvez incorporer pour ajouter vos propres règles d'analyse de code.

En utilisant l'analyse de code, si vous "oubliez" d'ajouter les délégués (bien que je ne vois pas l'intérêt de l'oublier, comme si le délégué n'est pas utilisé, ce n'est pas nécessaire), vous obtiendrez un avertissement / une erreur.

Iravanchi
la source
1
Vous avez peut-être raison, mais il y a des moments où vous faites des suppositions que même avec une bonne documentation, il est difficile de se souvenir après un certain temps. J'essaie de réutiliser mon code mais parfois je ne me souviens pas de ce que c'est le noyau et de ce qu'il ne fait pas (quand il s'agit de délégués - c'est difficile pour moi de voir la situation dans son ensemble ...)
Asaf
1

L'un de vos commentaires faisait référence au type de retour du gestionnaire d'événements. Êtes-vous plus préoccupé par le type de gestionnaire ou les données provenant de l'événement? Si c'est le dernier, cela peut aider. Sinon, cette solution ne suffira pas, mais peut vous aider à vous rapprocher de ce que vous recherchez.

Tout ce que vous avez à faire est de déclarer vos gestionnaires d'événements en tant que gestionnaires d'événements génériques à la fois dans l'interface et dans votre implémentation et vous pouvez personnaliser les résultats de retour.

Votre classe conrete ressemblerait à ceci:

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

Votre interface ressemblerait à ceci:

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

Maintenant que les arguments d'événement renvoient vos types, vous pouvez les accrocher dans n'importe quel gestionnaire que vous définissez.

Pour référence: https://msdn.microsoft.com/en-us/library/edzehd2t(v=vs.110).aspx

Ruben Soto
la source
0

L'interface héritée dans votre classe dérivée vous rappellera de définir et de relier les éléments que vous y avez déclarés.

Mais vous voudrez peut-être également l'utiliser explicitement et vous devrez toujours l'associer à un objet.

Par exemple, en utilisant un modèle d'inversion de contrôle:

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

Vous pouvez essayer quelque chose comme ça.

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);
  }
}
Latence
la source