LINQ中最难或者最容易被误解的方面是什么?

282

背景:在接下来的一个月里,我将会就涉及到或至少包含C#中的LINQ这一话题发表三次演讲。我想知道哪些主题值得重点关注,基于人们可能难以理解的问题,或者他们可能存在误解的问题。除了作为使用表达式树远程执行查询(通常是IQueryable)的示例外,我不会特别谈论LINQ到SQL或实体框架。

那么,你发现过什么使LINQ难以理解的问题?你看到有哪些误解?例如可以是以下任何一种情况,但请不要限制你自己!

  • C#编译器如何处理查询表达式
  • Lambda表达式
  • 表达式树
  • 扩展方法
  • 匿名类型
  • IQueryable
  • 延迟与立即执行
  • 流式与缓存执行(例如OrderBy是延迟但缓存)
  • 隐式类型的局部变量
  • 阅读复杂的泛型签名(例如Enumerable.Join

3
我很想知道你们什么时候会进行这些演讲,以及是否有在线观看的方式。 - Mark Heath
2
第一场演讲:10月30日哥本哈根,希望能录制整个过程。(全天!) 第二场演讲:11月19日晚上伦敦,伦敦.NET用户组,可能会谈到Push LINQ。 第三场演讲:11月22日雷丁,开发者开发者日,60分钟内实现LINQ to Objects。 - Jon Skeet
1
下投票者请添加解释性评论。 - Jon Skeet
2
@Jon,抱歉,但我需要关闭这个。 - Tim Post
3
@Tim:说得对,反正它也没有得到更多的回答了。不过就我个人而言,我认为它最终还是很有建设性的——我确实觉得看看人们觉得什么棘手很有用。尽管如此,现在我可能不会再问这个问题了…… - Jon Skeet
显示剩余6条评论
42个回答

271

延迟执行


12
好的 - 显然这是读者最喜欢的,对于这个问题来说这是最重要的事情。我还会加入“缓冲 vs 流媒体”作为相关内容,并且通常在书籍中没有被讨论得足够详细。 - Jon Skeet
10
真的吗?在学习 Linq 的时候,我被指出它的惰性加载特性很多次,但对我来说从未成为问题。 - Adam Lassek
26
同意ALassek的观点。MSDN文档清楚地说明了LINQ的惰性计算特性。也许真正的问题在于开发人员的惰性编程习惯... =) - Seiti
4
尤其是当你意识到它适用于 LINQ 到对象而不仅仅是 LINQ 2 SQL 时,当你看到调用 10 个 Web 方法以检索项目列表,而你已经在枚举同一项目列表时,并且你认为该列表已经被评估过。 - Simon_Weaver
5
了解yield语句及其工作原理对于深入理解LINQ来说非常关键。 - peSHIr
显示剩余5条评论

125

我知道延迟执行的概念应该已经深深地烙印在我的脑海里了,但是这个例子确实帮助我更好地理解了它:

static void Linq_Deferred_Execution_Demo()
{
    List<String> items = new List<string> { "Bob", "Alice", "Trent" };

    var results = from s in items select s;

    Console.WriteLine("Before add:");
    foreach (var result in results)
    {
        Console.WriteLine(result);
    }

    items.Add("Mallory");

    //
    //  Enumerating the results again will return the new item, even
    //  though we did not re-assign the Linq expression to it!
    //

    Console.WriteLine("\nAfter add:");
    foreach (var result in results)
    {
        Console.WriteLine(result);
    }
}

上面的代码返回以下内容:

Before add:
Bob
Alice
Trent

After add:
Bob
Alice
Trent
Mallory

2
我认为这篇博客是最好的解释。它涉及到延迟执行,虽然它已经存在了很长时间,但我仍然认为它非常有用。 - Phill

104

这里不仅仅只有LINQ可用于操作SQL,而且它的功能不止是语言内嵌的SQL解析器。


6
我厌倦了每个人都认为:/ - TraumaPony
40
并非所有人都知道LINQ to SQL是什么,即使我经常使用LINQ,我也不知道它是什么。请注意,本句话已经包含了“通俗易懂”和“不改变原意”的要求,因此无需进行任何修改或解释。 - Robert Rossney
2
当我试图使用LINQ解释某些事情时,其他人只是看着我说:“哦,我不会像那样使用LINQ,只用SQL”,这让我感到非常恼火。 - Nathan W
13
同意,很多人似乎不理解LINQ是一个通用工具。 - Matthew Olenik

86
大O符号。如果你不知道该怎么做,LINQ使得编写O(n^4)算法变得非常容易,而你可能没有意识到这一点。


16
举个例子怎么样? - hughdbrown
4
例如,也许他的意思是,一个Select子句中很容易包含多个Sum()运算符,每个Sum()运算符都会导致整个记录集再次被遍历。 - Rob Packwood
1

事实上,甚至值得了解大O符号是什么以及为什么它很重要,以及一些导致查询效率低下的示例。我认为这就是原帖作者建议的内容,但我还是想提一下。

编辑:刚意识到这篇帖子已经1.5年了 :-)
- zcrar70
7
这不会是O(n^x),而是O(xn),它只是O(n)。 - Malfist
3
如果尝试在没有使用连接运算符的情况下进行连接操作,将导致O(n^x)的结果: 从range1中选取i1,在range2中选取i2,在range3中选取i3,在range4中选取i4,条件为i1等于i2并且i3等于i4,最后选择新的{i1,i2,i3,i4}。我之前确实见过这种写法。它可以正常工作,但速度非常慢。 - MarkPflug

55

我认为Lambda表达式可以解析为表达式树和匿名委托的事实非常重要,这样您就可以将相同的声明性lambda表达式传递给IEnumerable<T>扩展方法和IQueryable<T>扩展方法。


2
同意。我是一名老手,当我开始编写自己的QueryProvider时,我才意识到这种隐式转换正在发生。 - TheSoftwareJedi

53

我意识到许多LINQ扩展方法(例如Single()SingleOrDefault()等)有接受lambda表达式的重载,但花费了我太长时间。

你可以这样做:

Single(x => x.id == id)

不需要说这个 - 一些糟糕的教程让我养成了这样做的习惯

Where(x => x.id == id).Single()

+1,非常不错。我会记在心里的。 - Pretzel
4
我也经常会忘记这个。Count()等其他函数也是如此。除了代码可读性的显而易见的优点外,您知道是否存在任何性能差异吗? - Justin Morgan
1
在大学里,我的讲师想要因为使用这些重载而扣分!但我已经证明他错了! - TDaver
12
听起来有些奇怪,但我更喜欢第二种语法。我觉得它更易读。 - Konamiman

40
在LINQ to SQL中,我经常看到人们不理解DataContext,它如何使用以及应该如何使用。太多人没有看到DataContext的本质,即一个工作单元对象,而不是持久化对象。
我曾多次看到人们试图将DataContext / Session设置为单例,而不是为每个操作创建新的实例。
还有一种概念,我看到很多混淆,那就是查询语法与表达式语法。在某个点上,我会使用最简单的语法,通常使用表达式语法。很多人仍然没有意识到,它们最终会产生相同的结果,因为查询最终会编译成表达式。

2
警告:工作单元可以是一个具有数据上下文为单例的小程序。 - graffic
15
不应在单例中使用DataContext,这样不安全。 - Aaron Powell
3
@Slace,并非所有程序都是多线程的,因此在许多“桌面”软件中将DataContext设置为单例是可以接受的。 - Ian Ringrose
2
我在第一次 LINQ to SQL 项目中使用 DataContext 作为单例时遇到了问题。我认为文档和书籍没有明确指出这一点。实际上,我认为名称可以改进,但我不确定如何改进。 - Roger Lipscombe
1
阅读了多次 ScottGu 在 Linq 上的文章,才让我理解透彻。 - Evan Plaice

34

我认为LINQ被误解的部分是它是一种语言扩展,而不是数据库扩展或构造。

LINQ远比LINQ to SQL更加强大。

现在我们大多数人都已经在集合中使用了LINQ,我们再也不会回头!

LINQ是自泛型2.0以来.NET最重要的功能之一,匿名类型则是3.0。

现在我们有了Lambda,我迫不及待地想要并行编程!


我甚至认为它比匿名类型更重要,甚至可能比泛型还要重要。 - Justin Morgan

26

我个人很想知道什么是表达式树,以及为什么需要了解它。


6
我认为了解表达式树及其存在的意义是值得的,但不必深入了解如何手动构建它们。(手动构建表达式树很麻烦,但编译器在转换 lambda 表达式时会做得很好。) - Jon Skeet
3
实际上,我在考虑写一些关于表达式树的博客文章(因为我确实“懂”它们)。我发现操作表达式树非常有用... - Marc Gravell
然而,我认为它们对Jon的演讲没有帮助;-p - Marc Gravell
3
我担心表达树会成为像yield语句一样的东西:一开始我不理解它的用途,但最终证明它非常有价值。 - Robert Rossney
1
Marc Gravell,我很想阅读你关于这个主题的博客文章。期待着它的发布。 - Alexandre Brisebois
显示剩余2条评论

20

1
你可能也会对这篇博客文章感兴趣:http://msmvps.com/blogs/jon_skeet/archive/2008/02/29/odd-query-expressions.aspx - Jon Skeet
很棒的文章,Jon(我最近才订阅了你的博客)。 - Aaron Powell

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接