Linq implicity类型范围变量

mgm*_*ick 7 c#

使用Linq,范围变量(e)可以从它来自的数组/集合(emps)中隐式输入,但是如果没有var关键字或类型,foreach语句不能做同样的事情.为什么是这样?

在ex1中,编译器知道e是Employee类型,没有给出var关键字或任何东西.为什么ex2中的foreach循环不能做同样的事情,你必须提供类型(无论是var还是某种类型).

EX1.

    Employee[] emps = {new Employee ( 1, "Daniel", "Cooley", 7, 57.98M };

    public void SortByLastname()
    {
      var sortedByLastname =
            from e in emps
            orderby e.LastName
            select e.FirstName;
    }
Run Code Online (Sandbox Code Playgroud)

EX2.

        foreach (Employee empl in emps)
        {
            Console.WriteLine("Employee " + empl);
        }
Run Code Online (Sandbox Code Playgroud)

这可能是过度分析,但我试图找到原因的底部.

答案很可能是Linq查询语法被设置为自动推断范围变量的类型而foreach语句不是.有人可以帮助解释为什么会这样吗?

Eri*_*ert 14

更新:这个问题是我的博客2012年6月25日的主题.谢谢你这个好问题!


使用Linq,范围变量可以从它来自的集合中隐式输入,但是如果没有var关键字,foreach语句不能做同样的事情.

那是正确的.

为什么是这样?

我永远不知道如何回答"为什么"的问题.所以我假装你问了一个不同的问题:

有两种不同的方式可以隐式输入命名变量.可以通过将"var"替换为其显式类型来隐式地键入用于循环变量,foreach循环变量或使用语句变量的命名局部变量.可以通过完全省略其类型来隐式地键入lambda参数或查询范围变量.

正确.

这是不一致的.一个基本的设计原则是要避免不一致,因为它令人困惑; 用户自然地认为不一致表达了意义.这些功能是否可以保持一致?

实际上,有两种方法可以保持一致.第一个是在任何地方都要求"var",所以你会说:

Func<double, double> f = (var x)=>Math.Sin(x);
var query = from var customer in customers
            join var order in orders on customer.Id equals ...
Run Code Online (Sandbox Code Playgroud)

所有设计都是一系列妥协.这符合一致性测试,但现在感觉笨重和冗长.

第二个是在任何地方消除"var",所以你会说:

x = 12; // Same as "int x = 12;"
using(file = ...) ... 
for(i = 0; i < 10; ++i) ...
foreach(c in customers) ... 
Run Code Online (Sandbox Code Playgroud)

在前三个案例中,我们现在无意中添加了"隐式声明的本地人"的特征而不是"隐式声明的本地人".有一个新的局部变量被声明只是因为你为以前没有使用过的名字指定了东西,这似乎很奇怪而且非C#.这是我们在JScript或VBScript等语言中所期望的那种功能,而不是C#.

但是,在foreach块中,从上下文中可以清楚地看到引入了局部变量.我们可以在这里消除"var"而不会引起太多的混淆,因为"in"没有被误认为是一个赋值.

好的,让我们总结一下我们可能的功能:

  • 特征1:无处不在需要var.
  • 特征2:要求var无处.
  • 特征3:要求局部变量,for循环和使用,而不是foreach循环,lambdas或范​​围变量
  • 特征4:要求本地变量,for循环使用和foreach,而不是lambdas或范​​围变量

前两个具有一致性的好处,但一致性只是一个因素.第一个是笨重的.第二个太动态和混乱.第三和第四个似乎是合理的妥协,尽管它们并不一致.

那么问题是:foreach循环变量更像是局部变量还是更像lambda参数?显然它更像是一个局部变量; 实际上,foreach循环被指定为重写,其中循环变量成为局部变量.为了与"for"循环保持一致,并且与foreach循环的C#1.0和C#2.0使用的一致性,这需要某种类型,我们选择选项4优于选项3.

我希望能回答你的问题.如果没有,那么问一些更具体的问题.


Jus*_*ony 3

您不需要列出类型的原因是因为它正在被分解(基本上)扩展方法。您应该能够将 ex2 重写为:

emps.ForEach(empl=>Console.WriteLine("Employee " + empl);
Run Code Online (Sandbox Code Playgroud)

请注意,您不需要明确说明类型,因为它是从emps

所以 ex1 将分解为:

emps.OrderBy(e=>e.LastName).Select(e=>e.FirstName);
Run Code Online (Sandbox Code Playgroud)

为了更全面地理解这一点以及更多内容,我强烈建议购买 Jon Skeets 的书C# In Depth Second Edition