2011-06-01 7 views
8

Estoy almacenando una acción en una variable local, luego estoy usando después de que la variable local esté fuera del alcance. ¿Corre el peligro de ser limpiado antes de usarlo? He aquí un ejemplo:Acción/Pregunta de administración de memoria de expresión de Lambda

public List<object> GetMaps() { 
    Action<Customer1, Customer2> baseMap = (Customer1 c1, Customer2 c2) => { 
     c2.FirstName = c1.FirstName; 
    }; 

    var list = new List<object>() { 
     new Action<SpecialCustomer1 c1, SpecialCustomer2 c2>() { 
      baseMap(c1, c2); 
      c2.SpecialProperty = c1.SpecialProperty; 
     }, 
     new Action<SpecialCustomer1 c1, SpecialCustomer2 c2>() { 
      baseMap(c1, c2); 
      c2.SpecialProperty2 = c1.SpecialProperty2; 
     }, 
    }; 

    return list; 
} 

Por lo tanto, se puede ver en este ejemplo que la función devuelve una lista de acciones que requieren baseMap. baseMap es solo una variable local. ¿El hecho de que se llame dentro de las otras acciones es suficiente para que .NET sepa que no debe limpiarlo?

Respuesta

11

remito a la sección 5.1.7 de la especificación C# 4, que dice:

Si la variable local es capturado por una función anónima , su duración se extiende al menos hasta que el delegado o el árbol de expresiones creados a partir de la función anónima, junto con cualquier otro objeto que venga a hacer referencia a la variable capturada, sean elegibles para la recolección de basura.

Incluso si el control supera el final del alcance del local, la vida útil del local se amplía. En la práctica, hacemos esto convirtiendo lo local en un campo de una clase de cierre, y luego manteniendo la clase viva haciendo referencia a ella en el delegado (o árbol de expresiones).

Tenga en cuenta que se puede encontrar con el problema opuesto; a veces las cosas viven más que los que ellos quieren:

Expensive expensive = new Expensive(); 
Cheap cheap = new Cheap(); 
Action longlived =()=>M(cheap); 
Action shortlived =()=>M(expensive); 

La forma en que esto funciona en C# Hoy es el cierre generada por los dos delegados mantiene ambos "barato" y "cara" con vida tanto tiempo como el tiempo de vida de la más larga ¡delegado vivo, a pesar de que el delegado de mayor duración en realidad no usa "caro"! (VB, JScript y muchos otros lenguajes que tienen cierres también tienen este problema.)

Lo que podríamos hacer aquí es detectar esta situación en el compilador y crear dos cierres. Estamos considerando hacer eso para una versión futura de C#.

5

No, no está en peligro. Un método anónimo que usa una variable local desde el exterior para que el método anónimo se compile en una nueva clase con un campo que guarda esa variable local y un método que corresponde al método anónimo.

En su caso, esto creará algo como lo siguiente:

class ModifiedClosure 
{ 
    private Action<Customer1, Customer2> _baseMap; 
    public ModifiedClosure(Action<Customer1, Customer2> baseMap) 
    { 
     _baseMap = baseMap; 
    } 

    public void Method(SpecialCustomer1 c1, SpecialCustomer2 c2) 
    { 
     _baseMap(c1, c2); 
     c2.SpecialProperty = c1.SpecialProperty; 
    } 
} 

La lista de inicialización busca entonces algo como esto:

Action<Customer1, Customer2> baseMap = (c1, c2) => c2.FirstName = c1.FirstName; 

var list = new List<object>() 
{ 
    (Action<SpecialCustomer1, 
      SpecialCustomer2>)(new ModifiedClosure(baseMap).Method), 
    // ... 
}; 

BTW: Su sintaxis es un poco apagado. La creación de la lista no se compilará. Debe tener un aspecto como este:

var list = new List<object>() 
{ 
    (Action<SpecialCustomer1, SpecialCustomer2>)((c1, c2) => 
    { 
     baseMap(c1, c2); 
     c2.SpecialProperty = c1.SpecialProperty; 
    }), 
    (Action<SpecialCustomer1, SpecialCustomer2>)((c1, c2) => 
    { 
     baseMap(c1, c2); 
     c2.SpecialProperty2 = c1.SpecialProperty2; 
    }) 
}; 
0

El delegado es el tipo de referencia y no se limpiará hasta que habrá por lo menos una referencia a ellos desde la raíz. Entonces, si crea el delegado de Acción y pasará el método delegado, no se preocupe por la limpieza.

1

No está fuera del alcance, ya que su lista hace referencia a él, por lo que no será limpiado por el recolector de basura.

Así que no, no estás en peligro