请考虑以下代码。看起来完全有效的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并且正在尝试创建数据结构库以供将来使用的人来说,这是一个非常烦人的错误...(我)
编辑:删除了错误的案例。
我将原始项目复制并删除到最低限度以实现此目的。这实际上是我新项目中的所有代码。
答案 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
(可能认为a
是ActionSurrogate
,这将符合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>();