Absenden floele 's Antwort; Für mehr Kontext, hier eine kurze Analyse des Problems:
Wenn der Compiler einen Iterator-Block verarbeitet, der eine IEnumerable
zurückgibt, generiert er eine private IEnumerable
Klasse, die die Iterationslogik enthält. Dies ist der Beginn des IL erzeugt für seine GetEnumerator
Verfahren durch den 4.0-Compiler:
.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
Beachten Sie die Anrufe zu System.Threading.Thread::get_CurrentThread()
und System.Threading.Thread::get_ManagedThreadId();
. Die generierte Methode verwendet dies, um eine Optimierung durchzuführen, bei der, wenn IEnumerable
sofort verbraucht wird [1], dieselbe Objektinstanz zurückgegeben wird (die Kosten eines Konstruktoraufrufs werden gespart).
Im Folgenden ist der IL durch den 4,5-Compiler generiert:
.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
Beachten Sie die zwei Anrufe von dem bisherigen Verfahren werden nun durch System.Environment::get_CurrentManagedThreadId()
ersetzt, die eine Eigenschaft 4.5 .NET hinzugefügt ist.
Da das 4.5-Upgrade den 4.0 C# -Compiler (csc.exe) überschreibt, verwendet für Version 4.0 auf Ihrem Computer kompilierter Code die neue IL-Vorlage und wird nicht auf einer Vanille 4.0-Installation ausgeführt, es sei denn, Sie haben die .NET 4.0-Referenz Assemblies [2], die bewirken, dass der Compiler die alte Version der IL generiert.
[1] Das heißt, das erste Mal, dass es in dem Thread verbraucht wird, der es erstellt hat (z. B. in einer foreach-Anweisung).
[2] Es ist tatsächlich möglich, den .NET 4.0-Compiler aus dem .NET Framework-Installationsprogramm zu extrahieren und Ihre Projektdateien zu ändern, um Ihren Code damit zu kompilieren. Dies könnte eine andere Möglichkeit sein, das Problem zu lösen, aber es ist eine lange Geschichte, und ich werde hier nicht ins Detail gehen
Dies erscheint auch als "Methode nicht gefunden:' Int32.System.Environment.get_CurrentManagedThreadId() '. – EBarr