为什么不能将匿名方法分配给var?

Mar*_*lon 136 c# lambda delegates implicit-typing .net-3.5

我有以下代码:

Func<string, bool> comparer = delegate(string value) {
    return value != "0";
};
Run Code Online (Sandbox Code Playgroud)

但是,以下内容无法编译:

var comparer = delegate(string value) {
    return value != "0";
};
Run Code Online (Sandbox Code Playgroud)

为什么编译器无法弄清楚它是Func<string, bool>什么?它需要一个字符串参数,并返回一个布尔值.相反,它给了我错误:

无法将匿名方法分配给隐式类型的局部变量.

我有一个猜测,那就是如果编译var版本,如果我有以下内容,它将缺乏一致性:

var comparer = delegate(string arg1, string arg2, string arg3, string arg4, string arg5) {
    return false;
};
Run Code Online (Sandbox Code Playgroud)

上面没有意义,因为Func <>只允许最多4个参数(在.NET 3.5中,这就是我正在使用的).也许有人可以澄清这个问题.谢谢.

Eri*_*ert 151

其他人已经指出,你可能有无数种可能的委托类型; 什么特别之处Func,它不愧为默认的替代PredicateAction或任何其他可能性?而且,对于lambdas,为什么明显的意图是选择委托形式,而不是表达树形式?

但我们可以说这Func是特殊的,并且lambda或匿名方法的推断类型是Func of something.我们仍然有各种各样的问题.您希望在以下情况下推断出哪些类型?

var x1 = (ref int y)=>123;
Run Code Online (Sandbox Code Playgroud)

没有任何Func<T>类型需要参考任何东西.

var x2 = y=>123;
Run Code Online (Sandbox Code Playgroud)

我们不知道形式参数的类型,尽管我们知道返回.(或者我们呢?返回int?long?short?byte?)

var x3 = (int y)=>null;
Run Code Online (Sandbox Code Playgroud)

我们不知道返回类型,但它不能无效.返回类型可以是任何引用类型或任何可以为null的值类型.

var x4 = (int y)=>{ throw new Exception(); }
Run Code Online (Sandbox Code Playgroud)

同样,我们不知道返回类型,这次它可能是无效的.

var x5 = (int y)=> q += y;
Run Code Online (Sandbox Code Playgroud)

这是否是一个返回void的语句lambda或返回分配给q的值的东西?两者都是合法的; 我们应该选择哪个?

现在,您可能会说,嗯,只是不支持任何这些功能.只需支持可以解决类型的"正常"情况.这没有用.这怎么能让我的生活更轻松?如果该功能有时有效并且有时会失败,那么我仍然必须编写代码来检测所有这些失败情况,并为每个失败情况提供有意义的错误消息.我们仍然必须指定所有行为,记录它,为它编写测试,等等.这是一个非常昂贵的功能,可以为用户节省六个键击.我们有更好的方法来增加语言的价值,而不是花费大量时间为一部分时间不起作用的功能编写测试用例,并且在它工作的情况下几乎没有提供任何好处.

它实际上有用的情况是:

var xAnon = (int y)=>new { Y = y };
Run Code Online (Sandbox Code Playgroud)

因为那件事没有"可说的"类型.但是我们一直都有这个问题,我们只是使用方法类型推断来推断出类型:

Func<A, R> WorkItOut<A, R>(Func<A, R> f) { return f; }
...
var xAnon = WorkItOut((int y)=>new { Y = y });
Run Code Online (Sandbox Code Playgroud)

现在方法类型推断可以解决func类型的问题.

  • 你什么时候将你的SO答案编成一本书?我买了:) (41认同)
  • @Eric:很好的答案,但它有点*误导性地说明这是不可能的事情,因为这实际上在D中完全正常.只是你们没有选择给代表文字他们自己的类型,而是做了他们依赖于他们的背景...所以恕我直言,答案应该是"因为这就是我们如何制造它"而不是其他任何东西.:) (22认同)
  • 我提出了Eric Lippert关于SO答案的书的提议.推荐标题:"堆叠的反射" (13认同)
  • @AbstractDissonance:我们根据有限的资源来衡量成本:开发人员和时间.这个责任不是由上帝授予的; 它是由开发部门副总裁强加的.不知何故C#团队可以忽略预算流程的想法是一个奇怪的想法.我向您保证,通过对C#社区表达了愿望的专家进行认真,深思熟虑的考虑,微软的战略使命以及他们自己在设计方面的出色品味,仍然可以做出权衡. (6认同)
  • @nawfal:你错过了我的观点.写那个歧义检查器也*工作.在C#中编写重载决策算法最困难的部分不是编写它来使*正确*案例工作.能够解决代码错误时给出的错误*的代码比解决正确情况的代码更长更复杂.歧义检查器非常难以正确*因为代码含糊不清*. (4认同)
  • @abstractdissonance我还注意到编译器是开源的.如果您关心此功能,那么您可以捐出必要的时间和精力来实现这一目标.我鼓励你提交拉取请求. (4认同)
  • +1很棒的答案.每个人都叫你的名字,我们知道你会出现. (3认同)
  • @EricLippert这不是一个答案,那就是警察。如果您想使用该逻辑,则可以使用它来证明没有任何东西。抱歉,这是真的...即使您会尝试摆脱事实真相。 (2认同)
  • @abstractdissonance我向你保证这是一个答案.如果你不喜欢它的答案那么为什么不发布你喜欢的答案呢?这样,我和其他所有人都可以从您对明确关心的问题的见解中受益. (2认同)
  • @AbstractDissonance:最后,您的一般主张是我可以使用“功能有成本和收益,我们尝试仅实现那些以合理成本提供良好收益的功能”的逻辑来证明不做任何功能是合理的。**这是 100% 正确的**。我*确实*使用这种逻辑来证明做任何成本超过其收益的功能是合理的。从那里“蠕动”是没有道理的。我说过几十个——几百个?-- 在这个网站上,某些功能是有成本的,必须以它们的好处来证明是合理的。我不仅接受这个真理,而且一遍又一遍地强调它! (2认同)

ito*_*son 29

只有Eric Lippert肯定知道,但我认为这是因为委托类型的签名并不能唯一地确定类型.

考虑你的例子:

var comparer = delegate(string value) { return value != "0"; };
Run Code Online (Sandbox Code Playgroud)

以下是var应该是什么的两种可能的推论:

Predicate<string> comparer  = delegate(string value) { return value != "0"; };  // okay
Func<string, bool> comparer = delegate(string value) { return value != "0"; };  // also okay
Run Code Online (Sandbox Code Playgroud)

编译器应推断出哪一个?选择其中一个是没有充分理由的.虽然a Predicate<T>在功能上等同于a Func<T, bool>,但它们仍然是.NET类型系统级别的不同类型.因此,编译器无法明确地解析委托类型,并且必须使类型推断失败.

  • 是的 更糟糕的是,对于lambda,我们甚至都不知道它是否要使用委托类型。它可能是一个表达式树。 (2认同)

Bri*_*sen 6

他说,Eric Lippert有一篇关于它的老帖子

事实上,C#2.0规范称之为.方法组表达式和匿名方法表达式是C#2.0中的无类型表达式,lambda表达式在C#3.0中将它们连接起来.因此,他们在隐含声明的右侧显示"裸体"是非法的.


Ste*_*ary 5

不同的代表被认为是不同的类型.例如,Action不同于MethodInvoker,并且Action不能将实例分配给类型的变量MethodInvoker.

所以,给定一个匿名委托(或lambda)() => {},是一个Action还是一个MethodInvoker?编译器无法分辨.

类似地,如果我声明一个委托类型接受一个string参数并返回一个bool,编译器如何知道你真的想要一个Func<string, bool>而不是我的委托类型?它无法推断委托类型.