37 votos

Por qué compilador de C# se genera sola clase para capturar las variables de varias funciones lambda?

Supongamos que hemos dicho código:

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);
    }
}

Aquí action1 y action2 completamente separados conjunto de captura de las variables - rCopy fue creado especialmente para esto. Aún así, el compilador genera sólo una clase a la captura de todo (comprobado generado IL). Supongo que esto se hace por motivos de optimización, pero se le hace muy difíciles de detectar errores de pérdida de memoria: si a y b capturado en una sola clase, la GC es incapaz de recoger, al menos, tanto tiempo como cualquiera de las lambdas se hace referencia.

Es allí una manera de convencer compilador para producir dos diferentes captura de clases? O razón alguna por la que no se puede hacer?

P. S. un Poco más detallada, en mi blog: aquí y aquí.

33voto

Eric Lippert Puntos 300275

Se han vuelto a descubrir un atajo conocido en la implementación de funciones anónimas en C#. He descrito el problema en mi blog en 2007.

Es allí una manera de convencer compilador para producir dos diferentes captura de clases?

No.

O razón alguna por la que no se puede hacer?

No hay ningún teórico razón por la que un algoritmo mejorado para la partición de la cerrado-sobre las variables, de modo que se izó en cierre diferentes clases no podía ser concebido. No lo hemos hecho por razones prácticas: el algoritmo es complicado, costoso y caro para probar, y siempre hemos tenido más altas prioridades. Esperemos que eso cambie en Roslyn, pero estamos haciendo ningún tipo de garantía.

26voto

Hans Passant Puntos 475940

Estoy bastante seguro de que usted está viendo limitaciones prácticas en el compilador de reescritura del código de la lógica, no es sencillo de hacer. La solución es bastante fácil, crear la lambda en un método separado para obtener dos instancias independientes de la clase ocultos:

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));
}

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X