wysyłającego floele „s odpowiedź; Aby uzyskać więcej informacji, oto krótka analiza problemu:
Gdy kompilator przetwarza blok iteracyjny zwracający wartość IEnumerable
, generuje prywatną klasę IEnumerable
w celu zachowania logiki iteracji. Jest to początek IL generowane przez jego metody GetEnumerator
przez kompilator 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
Wskazówka rozmowy do System.Threading.Thread::get_CurrentThread()
i System.Threading.Thread::get_ManagedThreadId();
. Wygenerowana metoda wykorzystuje to do wykonania optymalizacji, gdzie, jeśli IEnumerable
zostanie zużyty natychmiast [1], zwracana jest ta sama instancja obiektu (zapisywanie kosztu wywołania konstruktora).
Poniżej IL wygenerowany przez kompilator 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
Wskazówka dwa telefony od poprzedniej metody są teraz zastąpione przez System.Environment::get_CurrentManagedThreadId()
, który jest własnością dodany w .NET 4.5.
Ponieważ aktualizacja 4.5 powoduje nadpisanie kompilatora 4.0 C# (csc.exe), kod skompilowany dla wersji 4.0 na twoim komputerze będzie używał nowego szablonu IL i nie będzie działał na instalacji wanilii 4.0, chyba że posiadasz .NET 4.0 Złożenia [2], które spowodują, że kompilator wygeneruje starą wersję IL.
[1] Oznacza to, że po raz pierwszy, że jest zużywana na gwint, który je wygenerował (na przykład w foreach).
[2] Jest to rzeczywiście możliwe, aby wyodrębnić .NET 4.0 kompilatora od instalatora .NET Framework i modyfikować pliki projektu do kompilacji kodu z tym. To może być kolejny sposób, aby rozwiązać ten problem, ale to długa historia i nie będę wchodzić w szczegóły tutaj
ten pojawia się również jako „Metoda nie znaleziono:' Int32.System.Environment.get_CurrentManagedThreadId() '. – EBarr