'委托'System.Action'不带0个参数。'这是一个C#编译器错误(lambdas +两个项目)吗?

时间:2010-12-17 01:04:29

标签: 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
                            };
    }
}

我收到编译错误'委托'动作'不接受0参数。使用(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#编译器错误?

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

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

我将原始项目复制并删除到最低限度以实现此目的。这实际上是我新项目中的所有代码。

3 个答案:

答案 0 :(得分:66)

最终更新:

该错误已在C#5修复。再次为此带来不便,并感谢报告。


原始分析:

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

我现在在咖啡店,而且我无法从这里访问编译器源代码。我将试着找一些时间在明天的调试版本中重现这一点,看看我是否可以解决正在发生的事情。如果我没有找到时间,我会在圣诞节之后离开办公室。

你观察到引入一个Action类型的变量导致问题消失是非常有趣的。编译器为性能原因和语言规范所需的分析维护了许多高速缓存。 Lambda和局部变量尤其具有许多复杂的缓存逻辑。我愿意打赌一些缓存正在初始化或填写错误的一美元,并且使用局部变量填充缓存中的正确值。

感谢您的报告!

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

我们很快就会发现。

更新:今天早上我的精神力量很强。当重载解析尝试确定是否存在带有零参数的委托类型的“Invoke”方法时,它会找到零Invoke方法以从中进行选择。我们应该确保在进行重载解析之前完全加载委托类型元数据。多久没有注意到这一点有多奇怪;它在C#3.0中的重复。当然,它不会仅仅因为没有lambda而在C#2.0中重现; C#2.0中的匿名方法要求您明确声明类型,这会创建一个本地,我们知道加载元数据。但我认为错误的根本原因 - 重载解析不会强制加载调用的元数据 - 返回到C#1.0。

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

答案 1 :(得分:23)

这可能是类型感染的问题,显然编译器将a推断为Action<T>而不是Action(可能认为aActionSurrogate ,这将符合Action<Action>>签名。请尝试明确指定a的类型:

    ActionSurrogate b = (Action a) =>
                        {
                            a();
                        };

如果不是这种情况 - 可以在项目周围检查任何自定义的Action代表参加一个参数。

答案 2 :(得分:2)

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


        }

这将编译。编译器有些故障无法找到没有参数的Action委托。这就是你得到错误的原因。

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>();