Petite note sur la réponse acceptée : Je ne suis pas d'accord avec une petite partie de Réponse de Jeffrey , à savoir le fait que, puisque Delegate
devait être un type de référence, il s'ensuit que tous les délégués sont des types de référence. (Il n'est tout simplement pas vrai qu'une chaîne d'héritage à plusieurs niveaux exclut les types de valeur ; tous les types enum, par exemple, héritent de System.Enum
qui hérite à son tour de System.ValueType
qui hérite de System.Object
, tous de référence). Cependant, je pense que le fait que, fondamentalement, tous les délégués héritent en fait non seulement de Delegate
mais de MulticastDelegate
est la réalisation critique ici. En tant que Raymond souligne dans un commentaire à son une fois que vous vous êtes engagé à prendre en charge plusieurs abonnés, il n'y a vraiment aucune raison pour que vous vous engagiez dans un programme de formation. no l'utilisation d'un type de référence pour le délégué lui-même, étant donné la nécessité d'un tableau quelque part.
Voir la mise à jour en bas de page.
Il m'a toujours semblé étrange que si je faisais cela :
Action foo = obj.Foo;
Je crée un nouveau Action
à chaque fois. Je suis sûr que le coût est minime, mais il implique l'allocation de mémoire qui sera ensuite ramassée par les éboueurs.
Étant donné que les délégués sont par nature eux-mêmes immuables, je me demande pourquoi ils ne pourraient pas être des types de valeurs ? Dans ce cas, une ligne de code comme celle qui précède n'entraînerait rien de plus qu'une simple affectation à une adresse mémoire sur la pile*.
Même en considérant les fonctions anonymes, il semble (pour moi ), cela fonctionnerait. Prenons l'exemple simple suivant.
Action foo = () => { obj.Foo(); };
Dans ce cas foo
constitue une fermeture , oui. Et dans de nombreux cas, j'imagine que cela nécessite un type de référence réel (comme lorsque les variables locales sont fermées et modifiées dans la fermeture). Mais dans certains cas, cela ne devrait pas être le cas. Par exemple, dans le cas ci-dessus, il semble qu'un type supportant la fermeture pourrait ressembler à ceci : Je retire ma remarque initiale à ce sujet. Le type ci-dessous doit vraiment être un type de référence (ou : il ne doit pas être un type de référence). besoin mais s'il s'agit d'un struct
il sera de toute façon mis en boîte). Ne tenez donc pas compte de l'exemple de code ci-dessous. Je le laisse uniquement pour fournir un contexte aux réponses qui le mentionnent spécifiquement.
struct CompilerGenerated
{
Obj obj;
public CompilerGenerated(Obj obj)
{
this.obj = obj;
}
public void CallFoo()
{
obj.Foo();
}
}
// ...elsewhere...
// This would not require any long-term memory allocation
// if Action were a value type, since CompilerGenerated
// is also a value type.
Action foo = new CompilerGenerated(obj).CallFoo;
Cette question a-t-elle un sens ? Selon moi, il y a deux explications possibles :
- L'implémentation correcte des délégués en tant que types de valeurs aurait nécessité un surcroît de travail et de complexité, étant donné que la prise en charge de choses telles que les fermetures que les faire modifier les valeurs des variables locales aurait de toute façon nécessité des types de référence générés par le compilateur.
- Il y a quelques autres raisons pour lesquelles, sous le capot, les délégués ne peut être mis en œuvre en tant que types de valeurs.
En fin de compte, je n'en perds pas le sommeil ; c'est juste quelque chose qui m'intrigue depuis un petit moment.
Mise à jour : En réponse au commentaire d'Ani, je comprends pourquoi les CompilerGenerated
dans mon exemple ci-dessus pourrait tout aussi bien être un type de référence, puisque si un délégué doit comprendre un pointeur de fonction et un pointeur d'objet, il aura de toute façon besoin d'un type de référence (au moins pour les fonctions anonymes utilisant des fermetures, puisque même si vous introduisez un paramètre de type générique supplémentaire - par exemple, Action<TCaller>
-(ce qui ne couvrirait pas les types qui ne peuvent pas être nommés). Cependant Tout cela me fait regretter d'avoir introduit dans la discussion la question des types générés par le compilateur pour les fermetures ! Ma principale question concerne délégués c'est-à-dire la chose avec le pointeur de fonction et le pointeur d'objet. Il me semble toujours que que peut être un type de valeur.
En d'autres termes, même si cette...
Action foo = () => { obj.Foo(); };
...nécessite la création de un (pour soutenir la fermeture et donner au délégué quelque chose à référencer), pourquoi exige-t-il la création d'un objet de type "référence" ? deux (l'objet supportant la fermeture plus les Action
délégué) ?
*Oui, oui, détail de la mise en œuvre, je sais ! Tout ce que je veux dire, c'est <em>stockage de la mémoire à court terme </em>.