System.MissingMethodException Int32 System.Environment. get_CurrentManagedThreadId()
Что может вызвать следующее исключение?
System.MissingMethodException Int32 System.Environment.get_CurrentManagedThreadId()
Этот вызов метода, по-видимому, генерируется компилятором С# для методов, дающих IEnumerable<>
.
Установлена .NET Framework v4.0 x86, а бинарный файл скомпилирован для v4.0. Любой процессор.
Ответы
Ответ 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 и изменить файлы проекта, чтобы скомпилировать ваш код. Это может быть другим способом решить проблему, но это долгая история, и я не буду вдаваться в подробности здесь.