【问题标题】:'Delegate 'System.Action' does not take 0 arguments.' Is this a C# compiler bug (lambdas + two projects)?'委托'System.Action' 不接受 0 个参数。这是一个 C# 编译器错误(lambdas + 两个项目)吗?
【发布时间】:2011-05-26 21:29:49
【问题描述】:

考虑下面的代码。看起来像完全有效的 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 并试图创建一个数据结构库以供将来使用的人来说,这是一个非常烦人的错误......(我)

编辑:删除错误的情况。

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

【问题讨论】:

  • 有趣的是,在您的第二个示例中,Class2 可以编译,而在您的第一个示例中,完全相同的代码会中断。
  • 您是否有可能在某处对Action 有不同的定义?
  • @Anon.: Class2 原本在 Project B 中,所以没有报错。我刚刚对其进行了测试,在项目 A 中使用不同的文件没有区别。
  • 我得到了一个更复杂的版本:stackoverflow.com/questions/3276578/… ... 并在 MS connect 上作为问题提交:connect.microsoft.com/VisualStudio/feedback/details/577938/… .... 但是,这是一个更简单的重现步骤.希望它会在 SP1 中得到修复。
  • 查看 Jon Skeet 发布的代码,您似乎遇到了完全相同的错误,这让我很好奇他们是否在阅读您的报告后发现了导致错误的原因...

标签: c# compiler-errors lambda compiler-bug


【解决方案1】:

最终更新:

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


原文分析:

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

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

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

感谢您的报告!

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

我们很快就会知道的。

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

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

【讨论】:

  • 别担心,寻找究竟是什么导致了这个错误的发生很有趣,Femaref 的回答为这个问题提供了一个很好的解决方法。如果您从编译器源代码中找到任何内容,我很乐意听到它:)
  • 看起来确实是问题所在:如果我添加 'Action x;'在给出错误的行之前,错误会消失,但添加 'Func x' 不会。
  • 我认为它被忽视了这么久并不奇怪:只有当你定义一个需要另一个 delegate 的 delegate 时它才会失败,然后继续在不同的项目中使用它作为lambda,在lambda之前的代码中,从不引用参数委托类型。如果我将声明类型替换为“Action”甚至“Action>”,则不会出现错误。
【解决方案2】:

这可能是类型推断的问题,显然编译器将a 推断为Action&lt;T&gt; 而不是Action(它可能认为a 是ActionSurrogate,这将适合Action&lt;Action&gt;&gt; 签名) .尝试明确指定a 的类型:

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

如果不是这种情况 - 可能会检查您的项目是否有任何自定义的 Action 代表采用一个参数。

【讨论】:

  • 这似乎不太可能,但这是我会尝试的第一件事。
  • 解决了这个问题。我没有想到明确命名 lambda 中的类型。我想这是类型推断失败。奇怪的是,通过在定义之前的代码中使用 lambda,不会发生错误...
  • 我不认为这是类型推断失败,因为 VS 完美提示我a 是Action,即使我没有明确命名类型。
  • @Danny:仅仅因为 Visual Studio 以这种方式显示它并不意味着编译器可以正确编译它......请参阅 Eric 的帖子了解明显的原因。
【解决方案3】:
    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>();

【讨论】:

  • 如果你把它放在不同的项目中,它不会编译。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-12
  • 1970-01-01
  • 2017-04-05
  • 1970-01-01
相关资源
最近更新 更多