为什么不能将一个匿名方法分配给var?
我有以下代码:
Func<string, bool> comparer = delegate(string value) { return value != "0"; };
但是,下面不编译:
var comparer = delegate(string value) { return value != "0"; };
为什么编译器不知道它是一个Func<string, bool>
? 它接受一个string参数,并返回一个布尔值。 相反,它给了我错误:
无法将匿名方法分配给隐式types的本地variables。
我有一个猜测,那就是如果var版本编译 ,它会缺乏一致性,如果我有以下几点:
var comparer = delegate(string arg1, string arg2, string arg3, string arg4, string arg5) { return false; };
以上是没有意义的,因为Func <>只允许最多4个参数(在.NET 3.5中,这是我使用的)。 也许有人可以澄清这个问题。 谢谢。
其他人已经指出,你可能意味着有无数的可能的委托types; Func
有什么特别的地方,它应该是默认的,而不是Predicate
或者Action
或者其他的可能性? 而对于lambdas,为什么显而易见的是select委托forms,而不是expression式树forms?
但是我们可以说Func
是特殊的,lambda或匿名方法的推断types是Func。 我们仍然有各种各样的问题。 对于以下情况,您希望推断哪些types?
var x1 = (ref int y)=>123;
没有任何Func<T>
types需要ref。
var x2 = y=>123;
我们不知道forms参数的types,虽然我们知道返回。 (或者我们?是返回int?long?short?byte?)
var x3 = (int y)=>null;
我们不知道返回types,但不能是无效的。 返回types可以是任何引用types或任何可为null的值types。
var x4 = (int y)=>{ throw new Exception(); }
再次,我们不知道返回types,这次可能是无效的。
var x5 = (int y)=> q += y;
这是打算是一个返回void的语句lambda或返回分配给q的值的东西吗? 两者都是合法的; 我们应该select哪一个?
现在,你可能会说,只是不支持任何这些function。 只要支持可以计算出types的“正常”情况。 这没有帮助。 这是如何让我的生活更轻松? 如果这个特性有时会起作用而且有时候会失败,那么我仍然需要编写代码来检测所有这些失败情况,并给出一个有意义的错误信息 。 我们仍然必须指定所有的行为,logging它,为它写testing,等等。 这是一个非常昂贵的function ,可以节省用户大概六打击键。 我们有更好的方法来增加语言的价值,而不是花费大量的时间编写testing用例,这个function在一半时间不工作,并且在其工作情况下几乎没有任何好处。
实际上有用的情况是:
var xAnon = (int y)=>new { Y = y };
因为那个东西没有“朗读”types。 但是我们一直都有这个问题,我们只是使用方法types推断来推导出types:
Func<A, R> WorkItOut<A, R>(Func<A, R> f) { return f; } ... var xAnon = WorkItOut((int y)=>new { Y = y });
现在方法types推断可以确定functypes是什么。
只有Eric Lippert知道,但我认为这是因为委托types的签名并不唯一确定types。
考虑你的例子:
var comparer = delegate(string value) { return value != "0"; };
这里有两个可能的推断什么var
应该是:
Predicate<string> comparer = delegate(string value) { return value != "0"; }; // okay Func<string, bool> comparer = delegate(string value) { return value != "0"; }; // also okay
编译器应该推断哪一个? 没有理由select一个或另一个。 尽pipePredicate<T>
在function上等价于Func<T, bool>
,但它们在.NETtypes系统的层次上仍然是不同的types。 因此,编译器无法明确地parsing委托types,并且必须使types推断失败。
埃里克·利珀特(Eric Lippert)在他的讲话中有一个旧post
事实上,C#2.0规范就是这样调用的。 方法组expression式和匿名方法expression式在C#2.0中是无typesexpression式,lambdaexpression式将它们join到C#3.0中。 因此,他们在隐式声明的右边出现“裸体”是非法的。
不同的代表被认为是不同的types。 例如, Action
与MethodInvoker
不同, Action
的实例不能分配给MethodInvoker
types的variables。
所以,给定一个匿名委托(或lambda)like () => {}
,它是一个Action
还是一个MethodInvoker
? 编译器不能告诉。
同样,如果我声明一个采用string
参数的委托types并返回一个bool
,编译器如何知道你真的想要一个Func<string, bool>
而不是我的委托types? 它不能推断委托types。
以下几点来自MSDN:隐式types局部variables:
- var只能在局部variables在同一个语句中声明和初始化时使用; 该variables不能被初始化为空,或者方法组或匿名函数。
- var关键字指示编译器从初始化语句右侧的expression式推断variables的types。
- 了解var关键字并不意味着“变体”,并且不表示该variables是松散types的,或者是后期绑定的。 这只是意味着编译器确定并分配最合适的types。
MSDN参考:隐式键入的局部variables
考虑以下关于匿名方法:
- 匿名方法使您可以省略参数列表。
MSDN参考:匿名方法
我怀疑由于匿名方法实际上可能有不同的方法签名,因此编译器无法正确推断出最适合的types。
这是怎么回事?
var item = new { toolisn = 100, LangId = "ENG", toolPath = (Func<int, string, string>) delegate(int toolisn, string LangId) { var path = "/Content/Tool_" + toolisn + "_" + LangId + "/story.html"; return File.Exists(Server.MapPath(path)) ? "<a style=\"vertical-align:super\" href=\"" + path + "\" target=\"_blank\">execute example</a> " : ""; } }; string result = item.toolPath(item.toolisn, item.LangId);