2008-09-29 18 views
22

Ce n'est manifestement pas apparaît comme si ce ne serait pas une bonne pratique. Quelqu'un peut-il expliquer pourquoi ce ne serait pas une bonne pratique ou comment cela fonctionne? Tout livre ou article fournissant une explication serait apprécié.Variables locales avec les délégués

//The constructor 
public Page_Index() { 

    //create a local value 
    string currentValue = "This is the FIRST value"; 

    //use the local variable in a delegate that fires later 
    this.Load += delegate(object sender, EventArgs e) { 
     Response.Write(currentValue); 
    }; 

    //change it again 
    currentValue = "This is the MODIFIED value"; 

} 

La valeur qui est sortie est la deuxième valeur « modification ». Quelle partie de la magie du compilateur fait ce travail? Est-ce aussi simple que de suivre la valeur sur le tas et de le retrouver plus tard?

[Modifier]: Compte tenu de certains des commentaires, changer la phrase originale ... une

+0

Il n'y a rien de mal à cette pratique. Il est juste plus avancé que les débutants ne le comprendraient. – leppie

+0

secondé; en fait, il peut faire un design très propre/élégant - mais vous devez comprendre les implications. –

+0

C'est vraiment très intéressant. Je ne pense pas que jouer avec des variables locales dans la portée du délégué serait une bonne pratique, mais vous apprenez quelque chose de nouveau tout le temps. – Hugoware

Répondre

27

currentValue n'est plus une variable locale: il est un capturé la variable. Cette compile à quelque chose comme:

class Foo { 
    public string currentValue; // yes, it is a field 

    public void SomeMethod(object sender, EventArgs e) { 
    Response.Write(currentValue); 
    } 
} 
... 
public Page_Index() { 
    Foo foo = new Foo(); 
    foo.currentValue = "This is the FIRST value"; 
    this.Load += foo.SomeMethod; 

    foo.currentValue = "This is the MODIFIED value"; 
} 

Jon Skeet a un très bon rédiger cela en C# in Depth, et séparés (pas aussi détaillé) discussion here. Notez que la variable currentValue est maintenant sur le tas, pas sur la pile - cela a beaucoup d'implications, notamment parce qu'elle peut maintenant être utilisée par divers appelants.

Ceci est différent de java: en Java, la valeur d'une variable est capturée. En C#, la variable elle-même est capturée.

+0

En plus de l'écrire en C# en profondeur (merci pour la prise!), J'ai un article comparant les fermetures C# et Java et expliquant pourquoi ils sont gentils: http://csharpindepth.com/Articles/ Chapter5/Closures.aspx –

+0

Ah - alors que vous écriviez ce commentaire, je modifiais ce qui précède pour l'inclure. Doublez la valeur ;-p –

+0

@Mark: Pour améliorer légèrement votre grande réponse, pourriez-vous échanger "variable * valeur *" par "* valeur * de la variable" dans votre dernière phrase? Puisque la variable s'appelle "currentValue", elle peut dérouter le lecteur (comme moi), en pensant d'abord que l'italique "* value *" fait référence au * nom * de la variable ("quelle variable? Celle appelée" value * ") – chiccodoro

0

Vous devez capturer la valeur de la variable dans la fermeture/délégué, sinon elle peut être modifiée, comme vous l'avez vu.

Affectez currentValue à une variable locale (à l'intérieur) au délégué.

2

Je suppose que plus la question que je pose est que comment est-il travaille avec une variable locale [MG edit: "Acq - ignorer ce ..." a été ajouté par la suite]

C'est le point; il n'est vraiment plus une variable locale - du moins, pas en termes de comment nous pensons normalement à eux (sur la pile, etc). Cela ressemble à un, mais ce n'est pas le cas.

Et pour info, re "pas une bonne pratique" - méthodes anonymes et les variables capturées sont en fait un outil incroyablement puissant, en particulier lorsque vous travaillez avec des événements. N'hésitez pas à les utiliser, mais si vous suivez cette voie, je vous recommande de prendre le livre de Jon pour vous assurer que vous comprenez ce qui se passe réellement.