2016-10-07 4 views
0

J'ai une situation où le code se comporte différemment lorsqu'il est compilé sur deux boîtes de développement différentes. Je ne peux pas comprendre pourquoi. Je ne suis pas sûr que les mots-clés décrivent le mieux ce problème.Fermeture de capture d'expression lorsqu'elle doit référencer l'instance de classe

Quand lapin est appelé à une source de boîte a la valeur

{() => Program.Names} 

Sur une autre source de boîte a la valeur

{() => value(NPC.Program+<>c__DisplayClass1_0).Names} 

Hole a toujours la valeur, peu importe quel ordinateur il est compilé.

{() => value(NPC.Program+<>c__DisplayClass1_0).closure} 

Je suis en train de comprendre pourquoi une machine a l'expression de la classe et l'autre machine a la valeur de la fermeture lors de l'appel de lapin. J'aimerais aussi savoir s'il existe un moyen de contrôler cela. Merci!

class Program 
{ 
    public static ObservableCollection<string> Names = new ObservableCollection<string>(); 

    static void Main(string[] args) 
    { 
     string closure = "closure"; 

     if(closure.Length > 0) 
     { 
      GoingDown(p => { 
       closure.ToString(); 
       Names = new ObservableCollection<string>(); 

       Rabbit(() => Names); 
       Hole(() => closure); 
      }, closure); 
     } 
    } 

    public static void Rabbit<SourceType>(Expression<Func<ObservableCollection<SourceType>>> source) 
    { 
     "Testing".ToString(); 
    } 

    public static void Hole(Expression<Func<object>> source) 
    { 
     "Testing".ToString(); 
    } 

    public static void GoingDown(Action<object> a, object target) 
    { 
     Action b =() => 
     { 
      a(target); 
     }; 

     b(); 
    } 
} 
+2

Quelles versions de C#, et quel framework .net, sur les deux systèmes? – RBarryYoung

Répondre

4

Première: comme le commentaire indique, il serait utile de savoir quelles versions de #/etc .NET/C produisent quel comportement.

Deuxième: quel comportement, le cas échéant, est correct? La spécification ne dit pas. Cela donne au compilateur une grande latitude pour implémenter lambdas en tant que délégués et arbres d'expression comme bon lui semble.

Troisièmement: quel est le meilleur? Il est clair qu'il n'est pas nécessaire de copier les noms de champs statiques dans une classe de fermeture. Je m'attendrais à ce que la version sans fermeture soit générée; Je soupçonne que la version qui génère une fermeture a un bug. Peut-être que le bug a été corrigé dans la version qui n'affiche pas la sémantique de fermeture.

Quatrièmement: pouvez-vous contrôler ce comportement? Évidemment oui; vous avez une machine qui a le comportement et une qui ne l'est pas, donc vous pouvez simplement choisir de couler la machine qui a le mauvais comportement au fond de l'océan, et utiliser la machine qui a le comportement désiré.

Alternativement: il n'est pas nécessaire d'utiliser l'implémentation de la sémantique lambda du compilateur. Les Lambdas sont un sucre syntaxique; N'hésitez pas à générer vos arbres d'expression "à la main" si vous n'aimez pas l'arbre d'expression que le compilateur vous donne.

+0

Plutôt que d'essayer de forcer lambdas à générer la version que vous aimez, ou à lancer la vôtre, je pense qu'il vaudrait mieux simplement modifier vos analyseurs d'expression pour fonctionner correctement avec n'importe quelle expression sémantiquement valide, plutôt que de la coder en dur implémentation fixe. Vous ne devez pas vous soucier des détails de l'implémentation privée, plutôt que d'essayer d'orchestrer les détails d'implémentation privés spécifiques que vous souhaitez. – Servy