150

我有以下代码:

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 中,这是我正在使用的)。也许有人可以澄清这个问题。谢谢。

4

7 回答 7

162

其他人已经指出,您可能意味着有无数种可能的委托类型;有什么特别之处Func以至于它应该成为默认值而不是PredicateAction任何其他可能性?而且,对于 lambdas,为什么选择委托形式而不是表达式树形式很明显?

但我们可以说这Func是特殊的,lambda 或匿名方法的推断类型是某种东西的 Func。我们仍然会遇到各种各样的问题。对于以下情况,您希望推断出哪些类型?

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

没有任何Func<T>类型可以引用任何东西。

var x2 = y=>123;

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

var x3 = (int y)=>null;

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

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

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

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

那是打算成为返回无效语句 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 类型是什么。

于 2011-02-11T06:58:17.280 回答
32

只有 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

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

于 2011-02-11T04:35:03.103 回答
6

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

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

于 2011-02-11T04:40:24.873 回答
5

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

那么,给定一个匿名委托(或 lambda)() => {},它是一个Action还是一个MethodInvoker?编译器无法判断。

同样,如果我声明一个带string参数并返回 a的委托类型bool,编译器怎么知道你真的想要 aFunc<string, bool>而不是我的委托类型?它无法推断委托类型。

于 2011-02-11T04:34:44.777 回答
2

以下几点来自 MSDN 关于隐式类型局部变量:

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

MSDN 参考:隐式类型的局部变量

考虑以下有关匿名方法的内容:

  1. 匿名方法使您能够省略参数列表。

MSDN 参考:匿名方法

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

于 2011-02-11T04:55:54.233 回答
1

我的帖子没有回答实际问题,但确实回答了以下基本问题:

“我如何避免输入一些像 fugly 的类型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作为参数的方法,在这种情况下,您必须输入该丑陋的字符串:/

于 2018-11-08T15:55:37.653 回答
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);
于 2013-12-19T08:07:55.743 回答