Estaba respondiendo una pregunta sobre la posibilidad de que los cierres (legítimamente) extendieran la vida útil de los objetos cuando me topé con un código genético extremadamente curioso por parte del compilador de C # (4.0 si eso importa).
La reproducción más corta que puedo encontrar es la siguiente:
- Cree una lambda que capture un local mientras llama a un método estático del tipo que lo contiene.
- Asigne la referencia delegada generada a un campo de instancia del objeto contenedor.
Resultado: el compilador crea un objeto de cierre que hace referencia al objeto que creó el lambda, cuando no tiene razón para hacerlo: el objetivo 'interno' del delegado es un método estático , y los miembros de la instancia del objeto de creación lambda no necesitan ser (y no) tocado cuando se ejecuta el delegado. Efectivamente, el compilador está actuando como el programador ha capturado this
sin razón.
class Foo
{
private Action _field;
public void InstanceMethod()
{
var capturedVariable = Math.Pow(42, 1);
_field = () => StaticMethod(capturedVariable);
}
private static void StaticMethod(double arg) { }
}
El código generado a partir de una compilación de lanzamiento (descompilado a C # 'más simple') se ve así:
public void InstanceMethod()
{
<>c__DisplayClass1 CS$<>8__locals2 = new <>c__DisplayClass1();
CS$<>8__locals2.<>4__this = this; // What's this doing here?
CS$<>8__locals2.capturedVariable = Math.Pow(42.0, 1.0);
this._field = new Action(CS$<>8__locals2.<InstanceMethod>b__0);
}
[CompilerGenerated]
private sealed class <>c__DisplayClass1
{
// Fields
public Foo <>4__this; // Never read, only written to.
public double capturedVariable;
// Methods
public void <InstanceMethod>b__0()
{
Foo.StaticMethod(this.capturedVariable);
}
}
Observe que el <>4__this
campo del objeto de cierre se rellena con una referencia de objeto pero nunca se lee (no hay razón).
Entonces, ¿qué está pasando aquí? ¿La especificación del idioma lo permite? ¿Es este un error / rareza del compilador o hay una buena razón (que claramente me falta) para que el cierre haga referencia al objeto? Esto me pone ansioso porque parece una receta para que los programadores felices (como yo) introduzcan sin saberlo extrañas pérdidas de memoria (imagínese si el delegado se usara como controlador de eventos) en los programas.
this
.Respuestas:
Eso seguro parece un error. Gracias por llamar mi atención. Lo miraré. Es posible que ya se haya encontrado y reparado.
fuente
Parece ser un error o innecesario:
Ejecuto su ejemplo en IL lang:
Ejemplo 2
en cl: (¡Nota! ¡ahora esta referencia se ha ido!)
Ejemplo 3:
en IL: (Este puntero está de vuelta)
Y en los tres casos el método-b__0 () - se ve igual:
Y en los 3 casos hay una referencia a un método estático, por lo que lo hace más extraño. Entonces, después de este pequeño análisis, diré que es un error / para nada bueno. !
fuente
Foo.InstanceMethod
se hace estático, ¿eliminaría esto también la referencia? Estaría agradecido de saberlo.Foo.InstanceMethod
también fuera estático, no habría ninguna instancia a la vista y, por lo tanto, no habría forma dethis
capturar ningún tipo de cierre.