40 votes

Pourquoi le compilateur C # génère-t-il une seule classe pour capturer les variables de plusieurs lambdas?

Supposons que nous avons un tel code:

public class Observer
{
    public event EventHandler X = delegate { };
}

public class Receiver
{
    public void Method(object o) {}
}

public class Program
{
    public static void DoSomething(object a, object b, Observer observer, Receiver r)
    {
        var rCopy = r;
        EventHandler action1 = (s, e) => rCopy.Method(a);
        EventHandler action2 = (s, e) => r.Method(b);
        observer.X += action1;
        observer.X += action2;
    }

    public static void Main(string[] args)
    {
        var observer = new Observer();
        var receiver = new Receiver();
        DoSomething(new object(), new object(), observer, receiver);
    }
}

Ici, action1 et action2 ont complètement séparé de l'ensemble de la capture de variables - rCopy a été créé spécialement pour cela. Encore, le compilateur génère une seule classe à la capture tout (vérifié généré IL). Je suppose que c'est fait pour des raisons d'optimisation, mais c'est très dur à l'endroit de fuite de mémoire bugs: si a et b capturées en classe unique, GC est pas en mesure de recueillir à la fois, au moins, aussi longtemps qu' aucune des lambdas sont référencés.

Est-il un moyen de convaincre compilateur pour produire deux différents capture des classes? Ni aucune raison pourquoi il ne peut être fait?

P. S. un Peu plus détaillée, dans mon blog: ici et ici.

35voto

Eric Lippert Points 300275

Vous avez retrouvé un défaut dans la mise en œuvre de fonctions anonymes en C#. J'ai décrit le problème dans mon blog en 2007.

Est-il un moyen de convaincre compilateur pour produire deux différents capture des classes?

Pas de.

Ni aucune raison pourquoi il ne peut être fait?

Il n'est pas théorique raison pourquoi une amélioration de l'algorithme de partitionnement fermé plus de variables, de sorte qu'ils sont hissés en clôture différentes classes n'a pas pu être mis au point. Nous n'avons pas fait pour des raisons pratiques: l'algorithme est complexe, coûteux et coûteux de test, et nous avons toujours eu d'autres priorités. J'espère que ça va changer dans Roslyn, mais nous faisons aucune garantie.

27voto

Hans Passant Points 475940

Je suis assez sûr que vous voyez des limitations pratiques dans la logique de réécriture du code du compilateur, ce n'est pas simple à faire. La solution de contournement est assez simple, créez le lambda dans une méthode distincte pour obtenir deux instances distinctes de la classe masquée:

 public static void DoSomething(object a, object b, Observer observer, Receiver r) {
    var rCopy = r;
    observer.X += register(r, a);
    observer.X += register(rCopy, b);
}
private static EventHandler register(Receiver r, object obj) {
    return new EventHandler((s, e) => r.Method(obj));
}
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X