Es error del compilador y muy raro. Déjame explicarte los detalles. Estaría muy feliz si algunos expertos lo aclaran.
Sí, es incorrecto capturar this
en ctor, pero la situación se calienta porque se utilizó this
dentro de un delegado anónimo. Normalmente, si el delegado anónimo no tiene cierre (no captura las variables externas), el compilador lo implementa como método estático de la misma clase. Sucedió aquí.Pero echemos un vistazo al código IL generado de ese método estático:
.method private hidebysig static bool <.ctor>b__0() cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor()
.maxstack 1
.locals init (
[0] bool CS$1$0000)
L_0000: nop
L_0001: ldloc.0
L_0002: call instance bool ConsoleApplication15.Derived::MyValue()
L_0007: stloc.0
L_0008: br.s L_000a
L_000a: ldloc.0
L_000b: ret
}
¿lo viste? observe más de cerca la línea L_0002
y la línea L_0001
. Hay dos cosas muy extrañas:
- tratamos de llamar al método
MyValue
contra bool
!
- El método se llamó como
call
pero no es estático, el compilador de C# normalmente llama a los métodos de instancia con callvirt
! Si se usó el callvirt
, esta llamada fallaría, porque callvirt
comprueba this == null
.
Ahora vamos a romperlo. Vamos a presentar el cierre y cambiar el código a:
public Derived(bool myValue) : base(delegate() { return myValue^this.MyValue(); }) {
this.myValue = myValue;
}
¡Y ahora todo está bien! ¡No, NRE! Se generó una clase anónima y sus campos capturan el cierre. En este caso se genera correcta IL:
.method public hidebysig instance bool <.ctor>b__0() cil managed
{
.maxstack 2
.locals init (
[0] bool CS$1$0000)
L_0000: nop
L_0001: ldarg.0
L_0002: ldfld bool ConsoleApplication15.Derived/<>c__DisplayClass1::myValue
L_0007: ldarg.0
L_0008: ldfld class ConsoleApplication15.Derived ConsoleApplication15.Derived/<>c__DisplayClass1::<>4__this
L_000d: call instance bool ConsoleApplication15.Derived::MyValue()
L_0012: xor
L_0013: stloc.0
L_0014: br.s L_0016
L_0016: ldloc.0
L_0017: ret
}
método se llama contra clausurado esto. (pero aún con call instance
, hmm)
¿por qué está definiendo una clase abstracta como privada? –
@Mitch: podría estar anidado, en cuyo caso sería perfectamente legal. –
@Marc Gravell: claro, pero una clase abstracta privada anidada no suena muy útil ... –