Подтвердить что ты не робот

System.MissingMethodException Int32 System.Environment. get_CurrentManagedThreadId()

Что может вызвать следующее исключение?

System.MissingMethodException Int32 System.Environment.get_CurrentManagedThreadId()

Этот вызов метода, по-видимому, генерируется компилятором С# для методов, дающих IEnumerable<>.

Установлена ​​.NET Framework v4.0 x86, а бинарный файл скомпилирован для v4.0. Любой процессор.

4b9b3361

Ответ 1

CurrentManagedThreadId - это свойство .NET 4.5, поэтому для запуска кода вам потребуется 4,5. См. блоки Iterator, отсутствующие методы и .NET 4.5 для анализа того, как эта проблема может возникнуть.

Короче:

Если вы создадите приложение (ориентированное на .NET 4.0) в системе с установленным .NET 4.5, оно будет использовать 4.5 в качестве основы для компиляции, потому что .NET 4.0 Framework всегда перезаписывается .NET 4.5.

Если ваше приложение также использует yield return, оно будет терпеть неудачу в системах, имеющих только 4.0, потому что реализация этого оператора использует новое свойство при компиляции для 4.5 Framework.

Чтобы решить эту проблему, убедитесь, что ваша система компилятора имеет 4.0 ссылочные сборки.

Ответ 2

Вторичный floele ответ; для более контекста, здесь краткий анализ проблемы:

Когда компилятор обрабатывает блок итератора, возвращающий IEnumerable, он генерирует закрытый класс IEnumerable для хранения итерационной логики. Это начало IL, сгенерированного для его метода GetEnumerator компилятором 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

Обратите внимание на вызовы System.Threading.Thread::get_CurrentThread() и System.Threading.Thread::get_ManagedThreadId();. Сгенерированный метод использует это для выполнения оптимизации, где, если IEnumerable потребляется немедленно [1], возвращается тот же экземпляр объекта (сохраняющий стоимость вызова конструктора).

Ниже приводится IL, сгенерированный компилятором 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

Обратите внимание, что два вызова из предыдущего метода теперь заменены на System.Environment::get_CurrentManagedThreadId(), который является свойством, добавленным в .NET 4.5.

Так как обновление 4.5 перезаписывает компилятор 4.0 С# (csc.exe), код, скомпилированный для версии 4.0 на вашем компьютере, будет использовать новый шаблон IL и не будет запускаться на установке vanilla 4.0, если у вас нет сборников .NET 4.0 Reference Assemblies [2], что заставит компилятор генерировать старую версию IL.

[1] Это первый раз, когда он потреблял поток, который его создал (например, в инструкции foreach).

[2] Фактически возможно извлечь компилятор .NET 4.0 из установщика .NET Framework и изменить файлы проекта, чтобы скомпилировать ваш код. Это может быть другим способом решить проблему, но это долгая история, и я не буду вдаваться в подробности здесь.