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));
}
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.
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);
Action<T>
Est exactement le même que delegate void ... (T t)
Func<T>
Est exactement le même que delegate T ... ()
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.
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.
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.
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));
}
}
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.