我有以下代码:
Func<string, bool> comparer = delegate(string value) {
return value != "0";
};
但是,以下内容无法编译:
var comparer = delegate(string value) {
return value != "0";
};
为什么编译器无法确定它是Func<string, bool>
?它需要一个字符串参数,并返回一个布尔值。相反,它给了我错误:
无法为匿名方法指定 隐式类型的局部变量。
我有一个猜测,那就是如果var版本编译,如果我有以下内容则会缺乏一致性:
var comparer = delegate(string arg1, string arg2, string arg3, string arg4, string arg5) {
return false;
};
自从Func&lt;&gt;以来,上述说法没有意义。最多只允许4个参数(在.NET 3.5中,我正在使用它)。也许有人可以澄清这个问题。感谢。
答案 0 :(得分:145)
其他人已经指出,你可以有无限多种可能的委托类型; Func
有什么特别之处,它应该是默认值而不是Predicate
或Action
或其他任何可能性?而且,对于lambdas,为什么明显的意图是选择委托形式,而不是表达式树形式?
但是我们可以说Func
是特殊的,并且lambda或匿名方法的推断类型是Func of something。我们仍然有各种各样的问题。您希望在以下情况下推断出哪些类型?
var x1 = (ref int y)=>123;
没有Func<T>
类型可以引用任何参考。
var x2 = y=>123;
我们不知道形式参数的类型,尽管我们知道返回。 (或者我们呢?返回int?long?short?byte?)
var x3 = (int y)=>null;
我们不知道返回类型,但它不能为空。返回类型可以是任何引用类型或任何可以为null的值类型。
var x4 = (int y)=>{ throw new Exception(); }
同样,我们不知道返回类型,而这次 可能无效。
var x5 = (int y)=> q += y;
这是否是一个返回void的语句lambda或返回分配给q的值的东西?两者都是合法的;我们应该选择哪个?
现在,您可能会说,嗯,只是不支持任何这些功能。只需支持可以解决类型的“正常”情况。这没有用。这怎么能让我的生活更轻松?如果该功能有时有效并且有时会失败,那么我仍然必须将代码写入 detect 所有这些失败情况,并为每个失败情况提供有意义的错误消息。我们仍然必须指定所有行为,记录它,为它编写测试,等等。这是非常昂贵的功能,可以为用户节省六次击键。我们有更好的方法来增加语言的价值,而不是花费大量时间为一部分时间不起作用的功能编写测试用例,并且在它工作的情况下几乎不提供任何好处。
实际有用的情况是:
var xAnon = (int y)=>new { Y = y };
因为那件事没有“可说的”类型。但是我们一直都有这个问题,我们只是使用方法类型推断来推断出类型:
Func<A, R> WorkItOut<A, R>(Func<A, R> f) { return f; }
...
var xAnon = WorkItOut((int y)=>new { Y = y });
现在方法类型推断可以解决func类型的问题。
答案 1 :(得分:29)
只有Eric Lippert肯定知道,但我认为这是因为委托类型的签名并不能唯一地确定类型。
考虑你的例子:
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
编译器应推断出哪一个?选择其中一个是没有充分理由的。虽然Predicate<T>
在功能上等同于Func<T, bool>
,但它们仍然是.NET类型系统级别的不同类型。因此,编译器无法明确地解析委托类型,并且必须使类型推断失败。
答案 2 :(得分:6)
Eric Lippert在他说
的地方有一个古老的post实际上是C#2.0规范 把它叫出来。方法组 表达式和匿名方法 表达式是无类型表达式 在C#2.0中,lambda表达式加入 他们在C#3.0。因此它是 非法让他们“赤身裸体”出现 隐含的右手边 声明。
答案 3 :(得分:5)
不同的代表被视为不同的类型。例如,Action
与MethodInvoker
不同,Action
的实例无法分配给MethodInvoker
类型的变量。
所以,给定一个像() => {}
这样的匿名委托(或lambda),它是Action
还是MethodInvoker
?编译器无法分辨。
同样,如果我声明一个委托类型采用string
参数并返回bool
,那么编译器如何知道你真的想要一个Func<string, bool>
而不是我的委托类型?它无法推断出委托类型。
答案 4 :(得分:2)
以下几点来自MSDN关于隐式类型局部变量:
MSDN Reference: Implicitly Typed Local Variables
考虑以下关于匿名方法:
MSDN Reference: Anonymous Methods
我怀疑由于匿名方法实际上可能有不同的方法签名,因此编译器无法正确推断出最适合分配的类型。
答案 5 :(得分:1)
我的帖子没有回答实际问题,但确实回答了以下潜在问题:
“如何避免输入像Func<string, string, int, CustomInputType, bool, ReturnType>
这样的笨拙类型?” [1]
作为我的懒惰/ hacky程序员,我尝试使用Func<dynamic, object>
-使用一个输入参数并返回一个对象。
对于多个参数,您可以像这样使用它:
dynamic myParams = new ExpandoObject();
myParams.arg0 = "whatever";
myParams.arg1 = 3;
Func<dynamic, object> y = (dynObj) =>
{
return dynObj.arg0.ToUpper() + (dynObj.arg1 * 45); //screw type casting, amirite?
};
Console.WriteLine(y(myParams));
提示:如果不需要返回对象,则可以使用Action<dynamic>
。
是的,我知道这可能与您的编程原则背道而驰,但这对我和某些Python编码人员来说都是有意义的。
我是代表的新手...只是想分享我学到的东西。
[1] 假定您没有调用需要预定义Func
作为参数的方法,在这种情况下,您将必须键入该难看的字符串: /
答案 6 :(得分:0)
怎么样?
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);