';代表';系统行动';不接受0个参数;这是一个C#编译器错误(lambdas+;两个项目)?

';代表';系统行动';不接受0个参数;这是一个C#编译器错误(lambdas+;两个项目)?,c#,compiler-errors,lambda,compiler-bug,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 paramet

考虑下面的代码。看起来像是完全有效的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
                            };
    }
}
我是不是在这里偶然发现了一个C#编译器错误

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

编辑:删除了铁质外壳


为了实现这一点,我复制并精简了我的原始项目。这实际上是我新项目中的所有代码。

这可能是类型推断的问题,显然编译器将
a
推断为
动作
,而不是
动作
(它可能认为
a
动作代理
,它适合
动作
签名)。尝试显式指定
a
的类型:

    ActionSurrogate b = (Action a) =>
                        {
                            a();
                        };
如果不是这种情况-可能会检查您的项目中是否有任何自定义的
操作
代理使用一个参数

    public static void ThisWontCompile()
        {
            ActionSurrogate b = (Action a) =>
            {
                a();
            };


        }
这将编译。编译器出现一些问题,无法找到没有参数的操作委托。这就是为什么会出现错误

public delegate void Action();
public delegate void Action<T>();
public delegate void Action<T1,T2>();
public delegate void Action<T1,T2,T3>();
public delegate void Action<T1,T2,T3,T4>();
公共委托无效操作();
公共委托无效操作();
公共委托无效操作();
公共委托无效操作();
公共委托无效操作();
最终更新: 该错误已在C#5中修复。再次为给您带来的不便表示歉意,并感谢您的报告


原始分析: 我可以用命令行编译器重现这个问题。它看起来确实像一只虫子。可能是我的错;很抱歉。(我编写了所有lambda-to-delegate转换检查代码。)

我现在在一家咖啡馆,我无法从这里访问编译器源代码。我将尝试在明天的调试构建中找到一些时间来重现这一点,并看看是否能够解决问题。如果我没有时间,我将离开办公室直到圣诞节之后

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

谢谢你的报道

更新:我现在在车上,它刚刚来到我身边;我想我知道到底出了什么问题。编译器是懒惰的,尤其是在处理来自元数据的类型时。原因是引用的程序集中可能有数十万种类型,并且不需要加载所有这些类型的信息。您可能会使用远低于1%的内存,所以我们不要浪费大量时间和内存加载您永远不会使用的东西。事实上,懒惰比这更深;一个类型在使用之前要经过几个“阶段”。首先知道它的名称,然后知道它的基类型,然后知道它的基类型层次结构是否有充分的基础(非循环的,等等),然后知道它的类型参数约束,然后知道它的成员,然后知道成员是否有充分的基础(覆盖相同签名的某些内容,等等)我敢打赌,转换逻辑在检查委托调用的签名是否兼容之前,无法调用表示“确保所有委托参数的类型都有其成员已知”的方法。但是生成局部变量的代码可能就是这样做的。我认为在转换检查期间,就编译器而言,操作类型甚至可能没有invoke方法

我们很快就会知道的

更新:今天早上我的灵力很强。当重载解析试图确定是否存在接受零参数的委托类型的“Invoke”方法时,它会找到零个可供选择的Invoke方法。在执行重载解析之前,我们应该确保委托类型元数据已完全加载。奇怪的是,这件事这么长时间都没有被注意到;它在C#3.0中重新编程。当然,它在C#2.0中不会仅仅因为没有Lambda而重新编程;C#2.0中的匿名方法要求显式声明类型,这将创建一个本地类型,我们知道它将加载元数据。但我可以想象,这个bug的根本原因——重载解析不会强制加载调用的元数据——可以追溯到C#1.0


无论如何,迷人的虫子,谢谢你的报告。显然你有一个解决办法。我将让QA从这里跟踪它,我们将尝试为C#5修复它。(我们错过了一个窗口。)

非常有趣的是,在您的第二个示例中,Class2编译,而在您的第一个示例中,完全相同的代码断开。您是否可能在某个地方对
操作有不同的定义?@Anon:Class2最初在项目B中,这就是为什么没有报告错误的原因。我刚刚测试了它,在项目a中使用不同的文件没有什么区别。我得到了一个更复杂的版本:。。。并在MS connect上作为问题提交:。。。。然而,这是一个简单得多的复制步骤。希望它能在SP1中得到修复。看看Jon Skeet发布的代码,你似乎遇到了完全相同的错误,这让我好奇他们是否在阅读你的报告后发现了错误的原因……这似乎不太可能,但这是我尝试的第一件事。这解决了问题。我没有想到在lambda中显式地命名类型。我猜这是一个类型推断失败。奇怪的是,通过在定义之前的代码中使用lambda,错误不会发生……我不认为这是类型推断失败,因为VS向我显示了完美的提示,
a
是一个
操作
,即使我没有显式地命名类型。@D
public delegate void Action();
public delegate void Action<T>();
public delegate void Action<T1,T2>();
public delegate void Action<T1,T2,T3>();
public delegate void Action<T1,T2,T3,T4>();