2012-03-21 85 views
7

使用Linq,范围变量(e)可以从它来自的数组/集合(emps)隐式地键入,但是foreach语句不能在没有var关键字的情况下执行相同的操作,或者类型。为什么是这样?Linq implicity输入的范围变量

在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; 
    } 

ex2。

 foreach (Employee empl in emps) 
     { 
      Console.WriteLine("Employee " + empl); 
     } 

这可能是过分分析,但我试图找到为什么这是这种情况的底部。

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

+0

我不相信ex1是正确的 - 它不会编译。你不是指Employee [] emps = new [] {new Employee(...)}; ...并且在你的第二个例子中... foreach应该没有问题推断T []或任何IEnumerable ...所以我不确定我是否理解你描述的问题。 – Jeff 2012-03-21 04:25:48

+0

对不起,先在这里发帖,以便习惯网站的工作方式。是的,第二个示例并不意味着与第一个坏的应该只是把数组放在foreach中而不是linq结果。 – mgmedick 2012-03-21 04:39:34

+0

我更新了你的代码,以反映这一点,我的回答:) – 2012-03-21 04:47:26

回答

3

你不需要列出类型,因为这是被分解(基本上)扩展方式的原因。你应该能够改写EX2为:

emps.ForEach(empl=>Console.WriteLine("Employee " + empl); 

请注意,您不必明说的类型,因为它是从emps

推断

所以EX1会分解:

emps.OrderBy(e=>e.LastName).Select(e=>e.FirstName); 

为了更充分地了解这个和更多,我真的建议购买Jon Skeets书C# In Depth Second Edition

+1

哇快速回应:)我更经常来这里。所以这是linq代码如何工作的问题。无论背后的代码是否支持linq,都能够推断出这一点(就像一个总是存在的不可见变量),而foreach语句不是这样吗?那是对的吗?它是否与IEnumberable作为linq的接口有关,而我读过的实际上并不是foreach的接口?感谢您的帮助。 – mgmedick 2012-03-21 04:31:30

+0

种类。我建议阅读扩展方法和泛型,然后再介绍两者如何在类型推断中相互作用。基本上,在我的例子中,emps.OrderBy ...确实运行了一个静态方法,它传递emps作为第一个参数。然后用它来找出其余的类型。这可以成为一个非常广泛的主题来解释这里,所以我会强烈建议进一步理解这个去获得我建议的书...在最低限度的一些谷歌搜索我提到的主题 – 2012-03-21 04:40:18

+0

好的谢谢,所以在lamens linq语法是使用数组对象的扩展方法。虽然foreach不知道对象,所以它需要类型。我远离基地?大声笑 – mgmedick 2012-03-21 04:55:52

0

它看起来像'empl'是第二个示例中的字符串,因为LINQ语句的select子句说e.FirstName。如果你想让它包括雇员列表,使用以下命令:

var sortedByLastname = 
    from e in emps 
    select e 
    orderby e.LastName; 
0

这与语言结构有关比变量类型推断。如果没有var关键字,foreach循环不能执行的原因是因为foreach(var empl in list)foreach(empl in list)意味着不同的事情(我甚至不确定第二个是合法的)。第一个分配一个新的变量,而第二个(如果合法的话)将使用一个已经存在的变量(因为原因必须正确输入)。然而,LINQ构造中的for是以这样一种方式制作的,即当你做for e in list时e被创建为一个变量。

14

UPDATE:This question was the subject of my blog on June 25th, 2012。感谢您的好问题!


使用LINQ,范围变量可以被隐式地从它是来自集合类型,但foreach语句不能做同样的事情,而不var关键字。

这是正确的。

这是为什么?

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

有两种不同的方式可以隐式键入命名变量。对于循环变量,foreach循环变量或using语句变量,可以通过将“var”替换为其显式类型来隐式键入命名局部变量。 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 ... 

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

二是要消除“VAR”无处不在,让你会说:

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

前三情况下,我们现在已经在不经意间加入“隐式声明的当地人”的功能,而不是“隐式类型当地人”。看起来很奇怪,而且不像C#一样,只是因为您将某个事物分配给以前没有使用过的名称而声明了一个新的局部变量。这是我们期望的一种功能,如JScript或VBScript,而不是C#。

但是,在foreach块中,从上下文可以清楚地看到引入了局部变量。我们可以在这里消除“var”而不会造成太多的混淆,因为“in”不会被误认为是一个任务。

OK,让我们总结一下我们实现的功能:

  • 特点1:需要VAR无处不在。
  • 功能2:要求var无处。
  • 特征3:需要在当地人变种,for循环和usings但不foreach循环,lambda表达式或范围变量
  • 特征4:使用和foreach,但不是lambda表达式或范围变量需要上当地人变种,for循环

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

接下来的问题是:是foreach循环可变更像局部变量或更像拉姆达参数?显然它更像是一个局部变量;实际上,foreach循环是指定的作为重写,其中循环变量成为局部变量。为了与“for”循环保持一致,并且与需要某种类型的foreach循环的C#1.0和C#2.0使用一致,我们选择option 4作为选项3的优先级。

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