什么可能导致以下异常?
System.MissingMethodException Int32 System.Environment.get_CurrentManagedThreadId()
这个方法调用似乎是由 C# 编译器为方法生成的IEnumerable<>
。
已安装 .NET Framework v4.0 x86,并为 v4.0 Any CPU 编译二进制文件。
CurrentManagedThreadId
是 .NET 4.5 属性,因此您需要 4.5 才能运行代码。请参阅迭代器块、缺少的方法和 .NET 4.5以分析此问题如何发生。
简而言之:
如果在安装了 .NET 4.5 的系统上构建应用程序(针对 .NET 4.0),它将使用 4.5 作为编译的基础,因为 .NET 4.0 框架总是被 .NET 4.5 覆盖。
如果您的应用程序随后也使用yield return
,它将在仅安装 4.0 的系统上失败,因为此语句的实现在为 4.5 框架编译时使用了一个新属性。
要解决它,请确保您的编译器系统具有 4.0 参考程序集。
附议 floele的回答;有关更多上下文,以下是对该问题的简要分析:
当编译器处理返回的迭代器块时IEnumerable
,它会生成一个私有IEnumerable
类来保存迭代逻辑。GetEnumerator
这是4.0 编译器为其方法生成的 IL 的开头:
.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],则返回相同的对象实例(节省构造函数调用的成本)。
以下是4.5编译器生成的IL:
.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 C# 编译器 (csc.exe),因此在您的机器上为 4.0 编译的代码将使用新的 IL 模板,并且不会在 vanilla 4.0 安装上运行,除非您有 .NET 4.0 参考程序集 [2] ,这将导致编译器生成旧版本的 IL。
[1] 也就是说,第一次在创建它的线程上使用它(例如在 foreach 语句中)。
[2] 实际上可以从 .NET Framework 安装程序中提取 .NET 4.0 编译器,然后修改您的项目文件以使用它编译您的代码。这可能是解决问题的另一种方法,但这是一个很长的故事,我不会在这里详细介绍