35

什么可能导致以下异常?

System.MissingMethodException Int32 System.Environment.get_CurrentManagedThreadId()

这个方法调用似乎是由 C# 编译器为方法生成的IEnumerable<>

已安装 .NET Framework v4.0 x86,并为 v4.0 Any CPU 编译二进制文件。

4

2 回答 2

51

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 参考程序集。

于 2013-03-22T12:33:43.207 回答
3

附议 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 编译器,然后修改您的项目文件以使用它编译您的代码。这可能是解决问题的另一种方法,但这是一个很长的故事,我不会在这里详细介绍

于 2016-04-21T02:46:47.093 回答