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

时间:2011-02-11 04:17:04

标签: c# .net-3.5 delegates lambda implicit-typing

我有以下代码:

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

7 个答案:

答案 0 :(得分:145)

其他人已经指出,你可以有无限多种可能的委托类型; Func有什么特别之处,它应该是默认值而不是PredicateAction或其他任何可能性?而且,对于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)

不同的代表被视为不同的类型。例如,ActionMethodInvoker不同,Action的实例无法分配给MethodInvoker类型的变量。

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

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

答案 4 :(得分:2)

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

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

    考虑以下关于匿名方法:

    1. 匿名方法可以省略参数列表。
    2. 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);