c# - “委托(delegate) 'System.Action' 不接受 0 个参数。”这是 C# 编译器错误(lambdas + 两个项目)吗?

标签 c# compiler-errors lambda compiler-bug

考虑下面的代码。看起来像完全有效的 C# 代码,对吗?

//Project B
using System;
public delegate void ActionSurrogate(Action addEvent);
//public delegate void ActionSurrogate2();
// Using ActionSurrogate2 instead of System.Action results in the same error
// Using a dummy parameter (Action<double, int>) results in the same error

// Project A
public static class Class1 {
    public static void ThisWontCompile() {
        ActionSurrogate b = (a) =>
                            {
                                a(); // Error given here
                            };
    }
}

我收到编译器错误“Delegate 'Action' does not take 0 arguments.”在指定位置使用 (Microsoft) C# 4.0 编译器。请注意,您必须在不同的项目中声明 ActionSurrogate 才能显示此错误。

它变得更有趣:

// Project A, File 1
public static class Class1 {
    public static void ThisWontCompile() {
        ActionSurrogate b = (a) => { a(); /* Error given here */ };
        ActionSurrogate c = (a) => { a(); /* Error given here too */ };
        Action d = () => { };
        ActionSurrogate c = (a) => { a(); /* No error is given here */ };
    }
}

我是不是在这里偶然发现了 C# 编译器错误?

请注意,对于非常喜欢使用 lambda 并试图创建数据结构库以供将来使用的人来说,这是一个非常烦人的错误...(我)

编辑:删除了错误的案例。

为了实现这一目标,我将原始项目复制并剥离到最低限度。这实际上就是我新项目中的所有代码。

最佳答案

最终更新:

该错误已在 C# 5 中修复。再次对给您带来的不便表示歉意,并感谢您的报告。


原文分析:

我可以使用命令行编译器重现该问题。它看起来确实像一个错误。这可能是我的错;对于那个很抱歉。 (我编写了所有 lambda 到委托(delegate)的转换检查代码。)

我现在在咖啡店里,无法从这里访问编译器源代码。明天我将尝试找时间在调试版本中重现此问题,看看我是否能弄清楚发生了什么。如果我没有时间,我会在圣诞节后离开办公室。

您观察到引入 Action 类型的变量会导致问题消失,这非常有趣。出于性能原因和语言规范要求的分析,编译器维护许多缓存。 Lambda 表达式和局部变量尤其具有许多复杂的缓存逻辑。我愿意赌一美元,某些缓存在这里被初始化或填充错误,并且局部变量的使用在缓存中填充了正确的值。

感谢举报!

更新:我现在在公共(public)汽车上,它就来找我了;我想我很清楚哪里出了问题。编译器惰性,尤其是在处理来自元数据的类型时。原因是引用的程序集中可能有数十万种类型,因此无需加载有关所有这些类型的信息。您可能会使用其中的不到 1%,所以我们不要浪费大量时间和内存来加载您永远不会使用的内容。事实上,懒惰比这更深;一个类型在被使用之前会经历几个“阶段”。首先它的名字是已知的,然后是它的基类型,然后是它的基类型层次结构是否有根据(非循环等),然后是它的类型参数约束,然后是它的成员,然后是成员是否有根据(覆盖覆盖某些东西)具有相同的签名,依此类推。)我敢打赌,转换逻辑无法调用“确保所有委托(delegate)参数的类型都知道它们的成员”的方法,然后它检查委托(delegate)调用的签名以确保兼容性。但是生成局部变量的代码可能那样做。我认为在转换检查期间,就编译器而言,Action 类型甚至可能没有 invoke 方法。

我们很快就会知道。

更新:今天早上我的精神力量很强。当重载决策试图确定是否存在采用零参数的委托(delegate)类型的“Invoke”方法时,它会找到零个可供选择的 Invoke 方法。在进行重载解析之前,我们应该确保委托(delegate)类型元数据已完全加载。这么长时间以来,人们一直没有注意到这一点,这真是太奇怪了;它在 C# 3.0 中重现。当然,它不会在 C# 2.0 中重现,因为没有 lambda; C# 2.0 中的匿名方法要求您显式声明类型,这会创建一个局部变量,我们知道它会加载元数据。但我认为该错误的根本原因 - 重载解析不会强制为调用加载元数据 - 可以追溯到 C# 1.0。

无论如何,令人着迷的错误,感谢您的报告。显然你有一个解决方法。我会让 QA 从这里跟踪它,我们会尝试为 C# 5 修复它。(我们错过了 Service Pack 1, which is already in beta 的窗口。)

关于c# - “委托(delegate) 'System.Action' 不接受 0 个参数。”这是 C# 编译器错误(lambdas + 两个项目)吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4466859/

相关文章:

java - 用于安装 R 包的备用编译器 : clang: error: unsupported option '-fopenmp'

r - 是什么导致我的 R 包仅在基于 linux 的构建中出现 "Must request at least one colour from a hue palette"错误?

python - scipy.integrate.quad() 不使用 lambda

python - 简化代码-根据运算符执行数学运算

c# - 什么用于 System.Windows.Forms.Control.ControlCollection 中的键?

c# - 在没有 NETCFSvcUtil 生成代理的情况下与 WCF 的 Compact Framework 通信

c - 在 C 中设置 CPU 亲和性的问题

python - Pandas Groupby Lambda函数多个条件/列

c# - 如何为枚举创建 EditorTemplate?

c# - 使用 linq 收集到字符串