为啥 C# 编译器会在此代码上崩溃?

Posted

技术标签:

【中文标题】为啥 C# 编译器会在此代码上崩溃?【英文标题】:Why does the C# compiler crash on this code?为什么 C# 编译器会在此代码上崩溃? 【发布时间】:2014-08-12 06:30:30 【问题描述】:

为什么下面的代码会使 .NET 编译器崩溃?在 csc.exe 4.0 版上测试过。

参见例如这里是不同版本的在线演示 - 它以相同的方式崩溃,同时它说不支持动态https://dotnetfiddle.net/FMn59S:

编译错误(第 0 行,第 0 列):内部编译器错误(地址 xy 处的 0xc0000005):可能的罪魁祸首是“转换”。

扩展方法在List<dynamic> 上运行良好。

using System;
using System.Collections.Generic;

static class F  
    public static void M<T>(this IEnumerable<T> enumeration, Action<T> action)

    static void U(C.K d) 
        d.M(kvp => Console.WriteLine(kvp));
    


class C  
    public class K : Dictionary<string, dynamic>

更新:这不会使编译器崩溃

static void U(Dictionary<string, dynamic> d)

    d.M(kvp => Console.WriteLine(kvp));

更新 2:http://connect.microsoft.com/VisualStudio/feedback/details/892372/compiler-error-with-dynamic-dictinoaries 中报告了相同的错误。该错误是针对 FirstOrDefault 报告的,但似乎编译器在应用于从 Dictionary 派生的类的任何扩展方法上崩溃,其中至少一个参数类型是 dynamic。请参阅 Erik Funkenbusch 对以下问题的更一般性描述。

更新 3:另一个非标准行为。当我尝试将扩展方法作为静态方法调用时,即F.M(d, kvp =&gt; Console.WriteLine(kvp));,编译器不会崩溃,但找不到重载: Argument 1: cannot convert from 'C.K' to 'System.Collections.Generic.IEnumerable&lt;System.Collections.Generic.KeyValuePair&lt;string,dynamic&gt;&gt;'

更新 4 - 解决方案(种类):Hans 勾勒出第二种解决方法,它在语义上等同于原始代码,但仅适用于扩展方法调用,不适用于标准调用。由于该错误很可能是由于编译器无法将具有多个参数(其中一个是动态的)的泛型类派生的类转换为其超类型,因此解决方案是提供显式转换。见https://dotnetfiddle.net/oNvlcL:

((Dictionary<string, dynamic>)d).M(kvp => Console.WriteLine(kvp));
M((Dictionary<string, dynamic>)d, kvp => Console.WriteLine(kvp));

【问题讨论】:

类名的好选择。 FWIW,罗斯林编译了这个。 @Henk Holterman 看来这个bug是一样的——FirstOrDefault也是扩展方法connect.microsoft.com/VisualStudio/feedback/details/892372/… 我很难弄清楚什么答案会真正回答“为什么我没有源代码的程序会崩溃?”这个问题?您打算从这个问题的任何答案中学到什么? @Eric - 我希望了解一些关于编译器内部工作原理的知识,并找出这个错误的实际程度,因为它出现的情况比我最初想象的要多。 【参考方案1】:

dynamic触发了不稳定,当你用object替换它时崩溃消失了。

这是一种解决方法,另一种是帮助它推断出正确的 T:

static void U(C.K d) 
    d.M(new Action<KeyValuePair<string, dynamic>>(kvp => Console.WriteLine(kvp)));

您找到的feedback report 是一个强匹配项,我会说无需提交您自己的文件。

【讨论】:

更不用说它已经在内部构建中修复了,而且似乎在 Roslyn 中修复了。 connect.microsoft.com/VisualStudio/feedback/details/814645/… 好吧,如果你正在编写解释器,那么不使用dynamic 就很难做到。目前我知道的最好的解决方法是定义需要Dictionary&lt;T, T2&gt; 的方法,但这也是非常糟糕的解决方案。当需要将一个具有多个参数的泛型类转换为另一个接口并且至少一个参数是动态的时,似乎会发生该错误。在一种情况下它会崩溃编译器(扩展方法),在另一种情况下它找不到重载。 呃,我的解决方法是完全合理的,只是帮助它推断类型。与 dynamic 一起工作正常,无需选择“T2”。为什么不想使用它? 原谅我的无知,你的第二个解决方法很好。与代表一起工作得很好。如果没有实际答案,编译器出了什么问题,我会选择这个作为答案。因此,如果只有 lambda 参数,编译器似乎会将类转换为错误的接口,对吧? C# 编译器在这段代码中有一个很难解决的问题,双类型推断与 dynamic 相结合是可替代的,因为 Dictionary 实现了协变接口,从而增加了它可能匹配的数量考虑。我讨厌自己编写这样的代码,不可能完全测试。底层崩溃只是一个 NullReferenceException,在 C# 程序中每天都会发生,当它发生在 C++ 中时,它看起来更丑陋。完全诊断它需要 Microsoft 工程师。简化它必须做的工作,崩溃就消失了。【参考方案2】:

嗯,关于为什么它会导致编译器崩溃的问题的答案,这是因为你遇到了一个错误,它会导致编译器崩溃。

VS2013 编译器显示“内部编译器错误(地址 012DC5B5 处的 0xc0000005):可能的罪魁祸首是 'TRANSFORM'”,很明显这是一个错误。

C0000005 通常是一个空指针,或引用未分配或已删除的内存。这是一般保护故障。

编辑:

这个问题也存在于几乎任何类型的多参数泛型类型中,其中任何参数都是动态的。例如它崩溃:

List<Tuple<string, dynamic>>

它也崩溃了

List<KeyValuePair<dynamic, string>>

但不会崩溃

List<dynamic>

但确实会崩溃

List<List<dynamic>>

【讨论】:

所以...这是重复的问题?答案部分在哪里? @HenkHolterman - 首先,当我回答这个问题时,问题没有包含相同的信息。他后来补充说。第二,答案是第一句话,明确的说“The answer to your question”。我知道这听起来很愚蠢,但这就是答案。问一个愚蠢的问题,你会得到一个愚蠢的答案。我们没有编译器的源代码,所以我们无法知道更多。 您可能想将此信息添加到问题中,但它并没有真正回答它。 @ErikFunkenbusch - 澄清一下,我对中断 0n13/14 和 SEH/VEH 有一般的了解,但这只是一些问题的表现。 Hans 的第二个解决方法更能说明错误的性质。

以上是关于为啥 C# 编译器会在此代码上崩溃?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 FxCop 会在此 C# 代码中发出有关溢出 (CA2233) 的警告?

为啥这个 C 程序会崩溃?它编译得很好[重复]

为啥VC++ 2010编译器在编译简单代码时会崩溃?

编译代码后它崩溃(不明白为啥)

为啥在 C# 10 中我会在初始化属性上收到编译器警告/错误 CS8618

C程序为啥会崩溃?