c# .net-4.0

c# - System.MissingMethodException Int32 System.Environment. get_CurrentManagedThreadId()



.net-4.0 (2)

¿Qué podría causar la siguiente excepción?

System.MissingMethodException Int32 System.Environment.get_CurrentManagedThreadId()

Este método parece ser generado por el compilador de C # para los métodos que producen IEnumerable<> .

.NET Framework v4.0 x86 está instalado y el binario está compilado para v4.0 Cualquier CPU.


CurrentManagedThreadId es una propiedad de .NET 4.5, por lo que necesitará 4.5 para ejecutar el código. Consulte Bloques iterador, métodos faltantes y .NET 4.5 para un análisis de cómo podría ocurrir este problema.

En breve:

Si construye su aplicación (dirigida a .NET 4.0) en un sistema con .NET 4.5 instalado, usará 4.5 como base para la compilación, porque .NET Framework siempre se sobrescribe con .NET 4.5.

Si su aplicación también utiliza el yield return , fallará en los sistemas que tengan solo 4.0 instalado porque la implementación de esta declaración usa una nueva propiedad cuando se compila para el Marco 4.5.

Para resolverlo, asegúrese de que su sistema de compilación tenga los ensamblados de referencia 4.0.


Resolviendo la respuesta de floele ; para más contexto, aquí hay un breve análisis del problema:

Cuando el compilador procesa un bloque iterador que devuelve un IEnumerable , genera una clase IEnumerable privada para contener la lógica de iteración. Este es el comienzo de la IL generada para su método GetEnumerator por el compilador 4.0:

.method private final hidebysig newslot virtual instance class [mscorlib]System.Collections.Generic.IEnumerator`1<string> ''System.Collections.Generic.IEnumerable<System.String>.GetEnumerator'' () cil managed { .custom instance void [mscorlib]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( 01 00 00 00 ) .override method instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> class [mscorlib]System.Collections.Generic.IEnumerable`1<string>::GetEnumerator() // Method begins at RVA 0x57848 // Code size 89 (0x59) .maxstack 6 .locals init ( [0] bool, [1] class DOT.Core.MiscHelpers/''<ReadLines>d__0'', [2] class [mscorlib]System.Collections.Generic.IEnumerator`1<string> ) IL_0000: call class [mscorlib]System.Threading.Thread [mscorlib]System.Threading.Thread::get_CurrentThread() IL_0005: callvirt instance int32 [mscorlib]System.Threading.Thread::get_ManagedThreadId() IL_000a: ldarg.0 IL_000b: ldfld int32 DOT.Core.MiscHelpers/''<ReadLines>d__0''::''<>l__initialThreadId'' IL_0010: bne.un IL_0027

Observe las llamadas a System.Threading.Thread::get_CurrentThread() y System.Threading.Thread::get_ManagedThreadId(); . El método generado utiliza esto para realizar una optimización donde, si IEnumerable se consume inmediatamente [1], se devuelve la misma instancia de objeto (lo que ahorra el costo de una llamada de constructor).

El siguiente es el IL generado por el compilador 4.5:

.method private final hidebysig newslot virtual instance class [mscorlib]System.Collections.Generic.IEnumerator`1<string> ''System.Collections.Generic.IEnumerable<System.String>.GetEnumerator'' () cil managed { .custom instance void [mscorlib]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( 01 00 00 00 ) .override method instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> class [mscorlib]System.Collections.Generic.IEnumerable`1<string>::GetEnumerator() // Method begins at RVA 0x4830c // Code size 64 (0x40) .maxstack 2 .locals init ( [0] class DOT.Core.MiscHelpers/''<ReadLines>d__0'' ) IL_0000: call int32 [mscorlib]System.Environment::get_CurrentManagedThreadId() IL_0005: ldarg.0 IL_0006: ldfld int32 DOT.Core.MiscHelpers/''<ReadLines>d__0''::''<>l__initialThreadId'' IL_000b: bne.un IL_002b

Observe que las dos llamadas del método anterior ahora se reemplazan por System.Environment::get_CurrentManagedThreadId() , que es una propiedad agregada en .NET 4.5.

Dado que la actualización 4.5 sobrescribe el compilador 4.0 C # (csc.exe), el código compilado para 4.0 en su máquina utilizará la nueva plantilla IL y no se ejecutará en una instalación vanilla 4.0, a menos que tenga los ensamblados de referencia .NET 4.0 [2] , lo que hará que el compilador genere la versión anterior de IL.

[1] Es decir, la primera vez que se consume en el hilo que lo creó (por ejemplo, en una declaración foreach).

[2] De hecho, es posible extraer el compilador de .NET 4.0 del instalador de .NET Framework y modificar los archivos de tu proyecto para compilar tu código con eso. Esta podría ser otra forma de resolver el problema, pero es una historia larga y no entraré en detalles aquí