web-dev-qa-db-fra.com

Action <T> vs événement délégué

J'ai vu des développeurs utiliser les codes ci-dessous très alternativement. Quelle est la différence exacte entre ceux-ci et lesquels sont conformes à la norme? Sont-ils identiques, comme Action et Func<T> est également délégué:

public event Action<EmployeeEventAgs> OnLeave;
public void Leave()
{
    OnLeave(new EmployeeEventAgs(this.ID));
}

CONTRE

public delegate void GoOnLeave(EmployeeEventAgs e);
public event GoOnLeave OnLeave;
public void Leave()
{
    OnLeave(new EmployeeEventAgs(this.ID));
}
58
Bhaskar

Fwiw, aucun des deux exemples n'utilise les conventions .NET standard. Le EventHandler<T> générique doit déclarer l'événement:

public event EventHandler<EmployeeEventArgs> Leave;

Le préfixe "On" doit être réservé à une méthode protégée qui déclenche l'événement:

protected virtual void OnLeave(EmployeeEventArgs e) {
    var handler = Leave;
    if (handler != null) handler(this, e);
}

Vous n'avez pas avez pour le faire de cette façon, mais n'importe qui reconnaîtra instantanément le modèle, comprendra votre code et saura comment l'utiliser et le personnaliser.

Et il a le grand avantage de ne pas être obligé de choisir entre une déclaration de délégué personnalisée et Action<>, EventHandler<> est le meilleur moyen. Ce qui répond à votre question.

51
Hans Passant

Les deux lignes de code suivantes sont presque équivalentes:

public event Action<EmployeeEventAgs> Leave;

par rapport à:

public event EventHandler<EmployeeEventAgs> Leave;

La différence réside dans la signature de la méthode du gestionnaire d'événements. Si vous utilisez la première approche avec l'action, vous pourriez avoir:

public void LeaveHandler(EmployeeEventAgs e) { ... }

et puis ceci:

obj.Leave += LeaveHandler;

Avec la deuxième approche, la signature de LeaveHandler doit être différente:

public void LeaveHandler(object sender, EmployeeEventAgs e) { ... }

Il est très important de noter que dans les deux cas, le mot clé event est utilisé pour déclarer le membre de l'événement. Un membre de l'événement a déclaré que cette méthode n'est pas simplement un champ de la classe, même si elle semble l'être. Au lieu de cela, le compilateur le crée en tant que propriété d'événement1. Les propriétés d'événement sont similaires aux propriétés normales, sauf qu'elles n'ont pas d'accesseurs get ou set. Le compilateur permet de les utiliser uniquement sur le côté gauche d'un += et -= affectations (ajout ou suppression d'un gestionnaire d'événements). Il n'y a aucun moyen de remplacer les gestionnaires d'événements déjà affectés , ou ( appeler l'événement en dehors de la classe qui déclare il.

Si le mot clé d'événement était manquant dans les deux exemples, vous pouvez effectuer les opérations suivantes sans erreur ni avertissement:

obj.Leave = LeaveHandler;

qui effacera tous les gestionnaires enregistrés et les remplacera par le LeaveHandler.

De plus, vous pouvez également effectuer cet appel:

obj.Leave(new EmployeeEventAgs());

Les deux situations ci-dessus sont considérées comme un anti-pattern , si vous avez l'intention de créer un événement. Un événement doit être invoqué uniquement par l'objet propriétaire et ne doit pas autoriser non traçable suppression d'abonnés. Le mot clé event est la construction programmatique du .NET qui vous aide à vous en tenir à l'utilisation correcte des événements.

Compte tenu de ce qui précède, je pense que de nombreuses personnes s'en tiennent à l'approche EventHandler car il est plus peu probable d'utiliser un EventHandler sans le mot clé event. Les actions ont un champ d'utilisation plus large, elles ne sont pas aussi naturelles lorsqu'elles sont utilisées comme événements. Ce dernier est, bien sûr, une opinion personnelle, car l'approche du gestionnaire d'événements est probablement devenue trop intégrée dans mes propres pratiques de codage. Pourtant, si les actions sont utilisées correctement, ce n'est pas un crime de les utiliser pour des événements.


1 Une propriété d'événement est ce que le compilateur génère automatiquement lorsqu'il voit du code comme celui-ci:

event EventHandler SomeEvent 

Il devient à peu près le même code que le suivant:

private EventHandler _someEvent; // notice the lack of the event keyword!
public event EventHandler SomeEvent
{
    add { _someEvent += value; }
    remove { _someEvent -= value; }
}

Invocations d'événements que nous écrivons comme ceci:

this.SomeEvent(sender, args);

sont convertis en ceci:

this._someEvent(sender, args);
28
Ivaylo Slavov

Action<T> Est exactement le même que delegate void ... (T t)

Func<T> Est exactement le même que delegate T ... ()

22
pdr

L'action n'est qu'un raccourci pour la déclaration de délégué complète.

public delegate void Action<T>(T obj)

http://msdn.Microsoft.com/en-us/library/018hxwa8.aspx

Lequel utiliser dépend des normes/style de codage de votre organisation.

6
Darryl Braaten

Oui, Action et Func sont simplement des délégués de commodité qui ont été définis dans la 3.5 clr.

Action, Func et lambdas ne sont que du sucre syntaxique et de la commodité pour l'utilisation des délégués.

Il n'y a rien de magique en eux. Plusieurs personnes ont écrit de simples bibliothèques de modules complémentaires 2.0 pour ajouter cette fonctionnalité au code 2.0.

4
Sky Sanders

Vous voudrez peut-être regarder ici, voir ce que le compilateur génère réellement pour Action est la meilleure description. Il n'y a aucune différence fonctionnelle dans ce que vous avez écrit, juste une syntaxe plus courte et plus pratique.

4
Nick Craver

En général, ils sont équivalents. Mais dans le contexte de l'utilisation d'un délégué pour le type d'un événement, la convention consiste à utiliser EventHandler (où T hérite EventArgs):

public event EventHandler<EmployeeEventArgs> Left;

public void Leave()
{
    OnLeft(this.ID);
}

protected virtual void OnLeft(int id)
{
    if (Left != null) {
        Left(new EmployeeEventArgs(id));
    }
}
3
Rafa Castaneda

Vous auriez pu écrire ces délégués génériques Action et Func vous-même, mais comme ils sont généralement utiles, ils les ont écrits pour vous et les ont collés dans les bibliothèques .Net.

0
Jason Kleban