Linq隐式输入范围变量

时间:2012-03-21 04:21:32

标签: 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;
    }

EX2。

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

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

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

4 个答案:

答案 0 :(得分:14)

更新:This question was the subject of my blog on June 25th, 2012。谢谢你提出的好问题!


  

使用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 ...

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

第二个是在任何地方消除“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”不会被误认为是作业。

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

  • 功能1:无处不在。
  • 功能2:要求var无处。
  • 功能3:需要var对本地,for循环和使用但不是foreach循环,lambdas或范​​围变量
  • 特征4:需要var对本地,对于循环使用和foreach,而不是lambdas或范​​围变量

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

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

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

答案 1 :(得分: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

答案 2 :(得分:0)

看起来'empl'是第二个示例中的字符串,因为LINQ语句的select子句表示e.FirstName。如果要包含员工列表,请使用以下命令:

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

答案 3 :(得分:0)

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