En C# gestionnaire d'événements, pourquoi faut-il de l ' “expéditeur” paramètre être un objet?
Selon Microsoft événement règles de nommage, le sender
paramètre en C# gestionnaire d'événement "est toujours de type objet, même s'il est possible d'utiliser un type spécifique".
Cela conduit à beaucoup de code de gestion des événements comme:
RepeaterItem item = sender as RepeaterItem;
if (item != null) { /* Do some stuff */ }
Pourquoi la convention conseiller contre le fait de déclarer un gestionnaire d'événements plus spécifiques, de type?
MyType
{
public event MyEventHander MyEvent;
}
...
delegate void MyEventHander(MyType sender, MyEventArgs e);
Ai-je raté une chasse aux sorcières?
Pour la postérité: je suis d'accord avec le sentiment général dans les réponses que la convention est à l'utilisation de l'objet (et pour transmettre les données via le EventArgs
), même lorsqu'il est possible d'utiliser un type spécifique, et dans le vrai monde de la programmation, il est important de suivre la convention.
Edit: appât pour la recherche: RSPEC-3906 règle "des Gestionnaires d'Événement doit avoir la signature correcte"
- En fait, tous les types en C# sont des objets...
- Pendant que vous y êtes évidemment correct, ma question est la raison pour laquelle la convention est de ne pas utiliser un type spécifique, si possible.
- Oui, une valeur de retour dans le gestionnaire d'événement semble malodorante. Que faire si il n'y a plus d'un gestionnaire?
- J'ai une discussion détaillée de l'utilisation d'un fort typée "expéditeur" du paramètre. En bref, il n'y a pas d'inconvénients à l'utilisation d'un fort typée, et il est 100% compatible pour C# (toutes les versions) et VB.NET comme de VB 2008. Voir ici: stackoverflow.com/questions/1046016/....
- 2017 mise à jour du lien vers Microsoft gestionnaire d'événement lignes directrices de conception (qui remplace le Microsoft événement règles de nommage pour l' .NET 1.1).
Vous devez vous connecter pour publier un commentaire.
Bien, c'est un modèle plutôt que d'une règle. Cela signifie qu'un composant peut avancer sur un événement de l'autre, en gardant l'original de l'expéditeur, même si ce n'est pas normal type de collecte de l'événement.
Je suis d'accord c'est un peu étrange, mais c'est probablement la peine de coller à la convention seulement pour la connaissance de l'amour. (La connaissance pour les autres développeurs, ce qui est.) Je n'ai jamais été particulièrement vif sur
EventArgs
moi-même (étant donné que sur son propre, il transmet aucune information), mais c'est un autre sujet. (Au moins, nous avonsEventHandler<TEventArgs>
maintenant - bien que si il y avait aussi unEventArgs<TContent>
pour la commune de situation où vous avez juste besoin d'une seule valeur pour être propagées.)EDIT: Il est le délégué plus générale, le but, bien sûr - un seul type de délégué peut être réutilisé dans de multiples événements. Je ne suis pas sûr d'acheter que de plus en plus de bonnes raisons - notamment à la lumière des génériques, mais je suppose que c'est quelque chose...
"Generic delegates are especially useful in defining events based on the typical design pattern because the sender argument can be strongly typed and no longer has to be cast to and from Object."
à partir de MSDNJe pense qu'il y a une bonne raison à cette convention.
Prenons (et développer) @erikkallen exemple:
C'est possible (et l'a été depuis .Net 1, avant de génériques) parce que la covariance est pris en charge.
Votre question est tout à fait logique, si vous allez de haut en bas- c'est à dire que vous devez l'événement dans votre code, si vous l'ajoutez à votre contrôle.
Toutefois, la convention est de faciliter la tâche lors de l'écriture de composants dans la première place. Vous savez que pour tout événement le modèle de base (object sender, EventArgs e) travaillera.
Lorsque vous ajoutez le cas où vous ne savez pas comment il va être utilisé, et vous ne voulez pas arbitrairement contraindre les développeurs à l'aide de votre composant.
Votre exemple d'un générique, fortement typé événement est de bon sens dans votre code, mais ne rentre pas avec d'autres composants écrits par d'autres développeurs. Par exemple, si ils veulent utiliser votre composante avec celles ci-dessus:
Dans cet exemple, le type supplémentaire-contrainte est juste de la création de la douleur pour la télécommande développeur. Ils ont maintenant de créer un nouveau délégué juste pour votre composant. Si ils sont en utilisant une charge de vos composants dont ils pourraient avoir besoin d'un délégué de chacun.
Je pense de la convention est la peine de suivre pour quoi que ce soit externe ou que vous vous attendez à être utilisé en dehors d'un proche nit équipe.
J'aime l'idée du générique de l'événement args - je l'ai déjà utiliser quelque chose de similaire.
RepeaterItem
) et puis... quoi? Le code frappe et tente le casting, elle renvoie la valeur null, alors rien ne se passe... si le gestionnaire n'est pas conçu pour traiter un type particulier d'objet en tant qu'expéditeur, alors pourquoi voudriez-vous d'être en mesure de joindre? Évidemment, il va y avoir des événements qui sont suffisamment génériques pour complètement justifier l'utilisation de l'object
avec votre logique, mais... pas aussi bien(?) J'ai peut-être raté quelque chose, alors n'hésitez pas à éclairer ce que cela pourrait être.EventArgs
type pouvez toujours utiliser un gestionnaire de défini avec uneEventArgs
paramètre, de même, un cas plus spécifiques, type de l'expéditeur queobject
pouvez toujours utiliser un gestionnaire de défini avec uneobject
paramètre. Votre exemple de ce qui ne fonctionne pas, cela fonctionnera correctement.MyType
) à unobject sender
param, mais si il y a un type explicite (direApiType sender
), alors vous ne pouvez pas passerMyType
dans. Il y a des raisons que vous pourriez vouloir restriction, et ce n'est pas une mauvaise pratique, mais l'OP a été de se demander pourquoiobject sender
était un modèle commun utilisé par défaut par Microsoft.J'utilise le délégué suivant lorsque je préférerais un typage fort de l'expéditeur.
Cela peut être utilisé de la manière suivante:
Génériques et l'histoire jouent un grand rôle, surtout avec le nombre de contrôles (etc) qui exposent des événements similaires. Sans les génériques, vous vous retrouvez avec beaucoup d'événements exposer
Control
, qui est en grande partie inutile:object
)Si l'on considère les génériques, puis de nouveau tout est bien, mais ensuite commencer à entrer dans les questions de l'héritage; si la classe
B : A
, des événements surA
êtreEventHandler<A, ...>
, et les événements surB
êtreEventHandler<B, ...>
? Encore une fois, très déroutant, difficile pour l'outillage, et un peu brouillon au niveau de la langue.Jusqu'à ce qu'il y est une meilleure option qui couvre l'ensemble de ces,
object
œuvres; les événements sont presque toujours sur des instances de classe, donc il n'y a pas de boxe etc - juste un plâtre. Et le casting n'est pas très lente.Je suppose que c'est parce que vous devriez être en mesure de faire quelque chose comme
Pourquoi faites-vous de la sécurité de fonte dans votre code? Si vous savez que vous n'utilisez que la fonction comme un gestionnaire d'événements pour le répéteur, vous savez que l'argument est toujours le bon type et que vous pouvez utiliser un lancement en fonte au lieu de cela, par exemple (Répétition)de l'expéditeur au lieu de (expéditeur comme Répétiteur).
Pas de bonne raison à tout, maintenant, il y a covarience et contravarience je pense que c'est bien d'utiliser un typage fort de l'Expéditeur. Voir la discussion dans ce question
Conventions n'existent que pour imposer l'uniformité.
Vous POUVEZ fortement du type de vos gestionnaires d'événements si vous le souhaitez, mais demandez-vous, si cela ne fournissent aucun avantage technique?
Vous devriez considérer que les gestionnaires d'événements n'est pas toujours nécessaire de jeter l'expéditeur... la plupart du code de gestion des événements que j'ai vu, dans la pratique, ne pas utiliser le paramètre sender. Il est là, SI c'est nécessaire, mais assez souvent il ne l'est pas.
Je vois souvent des cas où les différents événements sur les différents objets partagent une seule commune, gestionnaire d'événements, qui fonctionne parce que le gestionnaire d'événement n'est pas à l'expéditeur.
Si ces délégués ont été fortement typé, même avec une utilisation intelligente des génériques, il serait TRÈS difficile de partager un gestionnaire d'événement comme ça. En fait, par une forte saisissant de vous imposer l'hypothèse que les gestionnaires devraient attention à ce que l'expéditeur est, quand ce n'est pas la réalité pratique.
Je pense que vous devriez vous poser est de savoir pourquoi voudriez-vous fortement du type de la gestion des événements de délégués? Ce faisant souhaitez-vous être l'ajout d'importants avantages fonctionnels? Êtes-vous permettant une utilisation plus "cohérent"? Ou êtes-vous juste d'imposer des hypothèses et des contraintes, juste pour le plaisir de fort-de frappe?
Vous dire:
Est-il vraiment beaucoup de code?
Je vous conseille de ne jamais utiliser le
sender
paramètre à un gestionnaire d'événements. Comme vous l'avez remarqué, il n'est pas typé statiquement. Ce n'est pas nécessairement le direct de l'expéditeur de l'événement, parce que parfois, un événement est transmis. Donc, le même gestionnaire d'événement peut même pas obtenir le mêmesender
type d'objet à chaque fois qu'il est déclenché. Il est inutile de forme d'association implicite.Lorsque vous inscrivez à un événement, à ce stade, vous devez savoir quel objet, l'événement est sur, et c'est ce que vous êtes plus susceptibles d'être intéressés par:
Et rien d'autre spécifique à l'événement devrait être dans l'EventArgs dérivées de deuxième paramètre.
Fondamentalement la
sender
paramètre est un peu de l'historique de bruit, mieux éviter.J'ai posé une question similaire ici.
C'est parce que vous ne pouvez jamais être sûr de qui a déclenché l'événement. Il n'existe aucun moyen de restreindre les types de permis de tirer un certain événement.
Le modèle de l'aide de Gestionnaire d'événements(object sender, EventArgs e) est destiné à fournir pour tous les événements les moyens d'identifier la source de l'événement (l'expéditeur), et de fournir un conteneur pour tous les cas spécifique de la charge utile.
L'avantage de ce modèle est aussi qu'il permet de générer un certain nombre d'événements différents en utilisant le même type de délégué.
Comme pour les arguments de ce défaut de délégué...
L'avantage d'avoir un sac unique pour tous à l'état que vous souhaitez passer avec l'événement est assez évident, surtout si il ya de nombreux éléments dans cet état.
À l'aide de l'objet au lieu d'un type fort permet de passer de l'événement, éventuellement, à des assemblées qui n'ont pas de référence à votre type (dans ce cas, vous pouvez faire valoir qu'ils ne seront pas en mesure d'utiliser l'expéditeur de toute façon, mais c'est une autre histoire - qu'elles peuvent encore le cas).
Dans ma propre expérience, je suis d'accord avec Stephen Redd, très souvent, l'expéditeur n'est pas utilisé. Le seul cas que j'ai besoin d'identifier l'expéditeur est dans le cas de l'INTERFACE utilisateur, des gestionnaires, avec plusieurs contrôles partage le même gestionnaire d'événements (pour éviter la duplication de code).
Je pars de sa position, cependant, que je ne vois pas de problème pour définir fortement typé délégués, et de générer des événements avec fortement typé signatures, dans le cas où je sais que le gestionnaire ne sera jamais ayant la garde de l'expéditeur est (en effet, souvent, elle ne doit avoir aucune portée dans ce type), et je ne veux pas les inconvénients de la farce de l'état dans un sac (EventArg sous-classe ou générique) et le déballage. Si je n'ai que 1 ou 2 éléments dans mon état, je suis OK génération de la signature.
C'est une question de commodité pour moi: typage fort signifie que le compilateur me tient sur mes orteils, et il réduit le type de ramification comme
ce qui rend le code meilleur 🙂
Ceci étant dit, c'est juste mon avis. J'ai dévié souvent à partir du modèle recommandé pour les événements, et je n'ai pas souffert tout pour elle. Il est important de toujours être clair sur pourquoi c'est OK pour s'écarter d'elle.
Bonne question!
.
Bien, c'est une bonne question. Je pense que n'importe quel autre type pourrait utiliser votre délégué pour déclarer un événement, de sorte que vous ne pouvez pas être sûr que le type de l'expéditeur est vraiment "MyType".
J'ai tendance à utiliser un délégué spécifique de type pour chaque événement (ou un petit groupe d'événements similaires). L'inutile de l'expéditeur et eventargs simplement l'encombrement de l'api et détourner l'attention du fait pertinent de bits d'information. D'être capable de "suivre" les événements au sein de ces classes n'est pas quelque chose que je n'ai pas encore trouver utile - et si vous devez transférer des événements comme ça, d'un gestionnaire d'événement qui représente un autre type d'événement, puis être forcé à envelopper l'événement vous-même et fournir les paramètres appropriés est peu d'effort. Aussi, le transitaire a tendance à avoir une meilleure idée de comment "convertir" les paramètres d'événement que le récepteur final.
En bref, à moins que, il y a quelques appuyant sur l'interopérabilité de la raison, de vidage de l'inutile, des paramètres déroutants.