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

我有以下密码:

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 < > 只允许最多4个参数(在。NET 3.5,这就是我正在使用的)。也许有人能解释一下这个问题。谢谢。

46378 次浏览

不同的委托被认为是不同的类型。例如,Action不同于 MethodInvoker,而且 Action的实例不能被赋值给 MethodInvoker类型的变量。

那么,给定一个像 () => {}这样的匿名委托(或 lambda) ,它是 Action还是 MethodInvoker

类似地,如果我声明一个带有 string参数并返回 bool的委托类型,编译器如何知道您真正需要的是 Func<string, bool>而不是我的委托类型?它无法推断委托类型。

只有 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 类型系统。因此,编译器不能明确地解析委托类型,并且必须使类型推断失败。

埃里克 · 利伯特说那里有个旧的 邮寄

事实上,C # 2.0规范 方法组 表达式和匿名方法 表达式是无类型表达式 在 C # 2.0中,lambda 表达式加入 在 C # 3.0中。因此它是 让他们“裸体”出现在 隐式的右边 声明。

以下是 MSDN 中关于隐式类型局部变量的要点:

  1. Var 只能在同一语句中声明和初始化局部变量时使用; 该变量不能初始化为 null,也不能初始化为方法组或匿名函数。
  2. Var 关键字指示编译器从初始化语句右侧的表达式推断变量的类型。
  3. 重要的是要理解 var 关键字并不意味着“变量”,也不表示变量是松散类型的,或者是后期绑定的。它只是意味着编译器确定并分配最合适的类型。

MSDN 引用: 隐式类型的局部变量

考虑到以下关于匿名方法的问题:

  1. 匿名方法使您可以省略参数列表。

MSDN 引用: 匿名方法

我怀疑,由于匿名方法实际上可能具有不同的方法签名,编译器无法正确推断分配的最合适类型是什么。

更新: 这个答案是十多年前写的,应该被认为是有历史意义的; 在 C # 10中,编译器将推断出一些委托类型。


其他人已经指出,有无限多的可能的委托类型,你 可以已经意味着; 什么是如此特殊的 Func值得是默认的,而不是 PredicateAction或任何其他可能性?而且,对于 lambdas 来说,为什么选择委托形式而不是表达式树形式的意图是显而易见的呢?

但是我们可以说 Func是特殊的,推断出的 lambda 或匿名方法的类型是 Func。我们还是会有各种各样的问题。对于下列情况,您希望推断出哪些类型?

var x1 = (ref int y)=>123;

没有 Func<T>类型接受任何裁判。

var x2 = y=>123;

我们不知道形式参数的类型,但我们知道返回值。(是吗?返回值是整数吗?很久?短?字节?)

var x3 = (int y)=>null;

我们不知道返回类型,但它不可能是 void。返回类型可以是任何引用类型或任何可空值类型。

var x4 = (int y)=>{ throw new Exception(); }

同样,我们不知道返回类型,这次它的 可以是 void。

var x5 = (int y)=> q += y;

这是一个 void-return 语句 lambda 还是返回赋给 q 的值?两者都是合法的,我们应该选择哪一个?

现在,你可能会说,好吧,只是不支持任何这些特性。只是支持“正常”的情况下,类型可以工作出来。没用的。这对我的生活有什么好处?如果该功能有时工作,有时失败,那么我仍然必须写的代码,以 侦查所有这些失败的情况,并给每个 有意义的错误消息。我们仍然需要指定所有这些行为,记录它,为它编写测试,等等。这是一个 非常昂贵的功能,节省用户可能六个键击。我们有更好的方法来增加语言的价值,而不是花费大量时间为一个功能编写测试用例,这个功能一半时间都不能工作,而且在它确实工作的情况下几乎没有任何好处。

实际上有用的情况是:

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 类型是什么。

怎么样?

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);

我的文章没有回答实际的问题,但是它回答了潜在的问题:

“我怎样才能避免输入一些丑陋的类型,如 Func<string, string, int, CustomInputType, bool, ReturnType>?”[1]

作为一个懒惰的程序员,我尝试使用 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作为参数,在这种情况下,您必须键入那个恶心的字符串:/

其他的答案在编写的时候是正确的,但是从 C # 10.0(从2021年开始)开始,编译器 可以在这种情况下推断出一个合适的委托类型(像一些 Func<...>Action<...>或生成的委托类型)。

参见 C # 10特性-Lambda 改进

var comparer = delegate(string value) {
return value != "0";
};   // OK in C# 10.0, picks 'Func<string, bool>' in this case

当然,对于我们来说,更常见的语法是 =>,因此:

var comparer = (string value) => {
return value != "0";
};   // OK in C# 10.0, picks 'Func<string, bool>' in this case