Linq警告

时间:2009-03-17 01:32:15

标签: .net linq

Linq是.NET的一个很棒的补充,我发现它在很多情况下都很好用,即使我刚开始学习如何使用Linq。

然而,在我对Linq所做的阅读中,我发现开发人员需要注意一些微妙的事情,这可能会导致麻烦。

我已经提到了一个明确的警告,我发现这是延迟执行的结果。

所以我想知道,对Linq来说,Linq的新手应该知道哪些其他注意事项?

3 个答案:

答案 0 :(得分:5)

在foreach循环中构建查询

IEnumerable<char> query = "Not what you might expect";
foreach(char vowel in "aeiou")
{
    query = query.Where(c => c != vowel);
}

上面的代码只是因为延迟执行而从字符串中删除了“u”。

要删除所有元音,您需要执行以下操作:

IEnumerable<char> query = "Not what you might expect";
foreach(char vowel in "aeiou")
{
    char temp = vowel;
    query = query.Where(c => c != temp);
}

答案 1 :(得分:3)

我认为LINQ相当稳固,并没有太多重要的警告。我遇到的几乎每一个“问题”都是延迟执行的结果,这不是一个真正的问题,而是一种不同的思维方式。

我遇到的最大问题 - 在性能分析方面,LINQ是一个游戏规则改变者(或者至少是一个规则弯曲者)。延迟执行可能使得有时分析应用程序变得更加困难,并且还可以以意想不到的方式显着地改变运行时性能特征。某些LINQ操作看起来几乎是神奇的,它们的速度有多快,而其他LINQ操作比我预期的要长得多 - 但从代码或分析器结果来看并不总是显而易见。

话虽如此,一般来说,延迟执行可以弥补手动编码例程减慢的情况。我更喜欢更简单,更清晰的代码到它替换的代码。

另外,我发现我使用LINQ to Objects越多,我就越需要重新思考我的设计并重新修改我的集合。

例如,在我开始经常使用linq对象之前,我从未意识到我经常暴露IList而不是IEnumerable。我现在完全理解为什么MS设计指南经常警告不要使用IList(例如,不要仅为Count属性返回IList等)。当我有使用IList的方法时,从linq查询中传递IEnumerable结果需要.ToList()或重新处理方法的API。

但是几乎总是值得重新思考 - 我发现许多地方传递了一个可枚举的并使用LINQ导致了巨大的性能。收益。如果您考虑延迟执行是很好的,并充分利用它。例如,使用.Take()将集合限制为前2个元素,如果所需要的那些在前linq上更具挑战性,并且大大加快了我的一些恶意循环。

答案 2 :(得分:1)

好问题。正如里德所指出的那样,他们都主要来自deferred execution(但与他不同的是,我觉得这是一个缺点。只是想到为什么不能通过记忆国家来推迟执行)。以下是几个示例 - 所有这些都是延迟执行问题的或多或少变体。

1)我懒得按时做某事

  

Linq仅在需要时执行。

一个常见的错误新手(我自己过去包括)make不知道延迟执行。例如,像

这样的东西
 var p = listOfAMillionComplexItems.OrderBy(x => x.ComplexProperty);

快速运行,但实际排序在枚举列表之前没有完成,换句话说,在您需要执行结果之前,执行没有完成。要执行它,您需要以下内容:

foreach(var item in p)...
//or
p.Count();
//or
p.ToList();
//etc

将它们视为SQL查询。如果你有

var query = from i in otherValues where i > 5 select i;

认为它类似于写作

string query = "SELECT i FROM otherValues WHERE i > 5";

后者是否会调用db?不,你必须

Execute(query);

这与Linq一样。

2)我住在现在

  

对Linq表达式中的变量进行更改时要小心谨慎   以后。

为了安全起见,首先备份变量然后在查询中使用备份,如果变量可以在实际执行查询之前更改。

From here:

decimal minimumBalance = 500;
var customersOver500 = from c in customers 
                       where c.Balance > minimumBalance 
                       select c;

minimumBalance = 200;
var customersOver200 = from c in customers
                       where c.Balance > minimumBalance 
                       select c;

int count1 = customersOver500.Count();
int count2 = customersOver200.Count();

假设我们有四个客户,余额如下:100,300,400和600. count1和count2是什么?他们都是3.&#34; customersOver500&#34;引用&#34; minimumBalance&#34;变量,但是在查询结果被迭代之前(通过for / each循环,ToList()调用或者甚至是&#34; Count()&#34;调用,都不会获得该值,如上所示) 。在使用该值处理查询时,minimumBalance的值已更改为200,因此两个LINQ查询都会生成相同的结果(余额超过200的客户)。

3)我的记忆力太弱,无法记住过去的贵重物品

  

与上述相同,背景略有不同。

或来自同一网站:

考虑一个使用LINQ-to-SQL获取客户列表的方法的简单示例:

public IEnumerable<Customer> GetCustomers()
{
    using(var context = new DBContext())
    {
        return from c in context.Customers
               where c.Balance > 2000
               select c;
    }
}

看起来非常无害 - 直到你得到一个&#34; ObjectDisposedException&#34;当您尝试枚举集合时。为什么?因为在尝试枚举结果之前,LINQ实际上并不执行查询。当此调用退出时,将丢弃DBContext类(公开Customers集合)。尝试枚举整个集合后,将引用DBContext.Customers类,并获得异常。

4)不要试着抓住我,我可能还是会溜走

  

如果没有明智地使用,try-catch对于声明来说毫无意义。

相反,全局异常处理在这里会更好。

try
{
    wallet = bank.Select(c => Convert.ToInt32(""));
}
catch (Exception ex)
{
    MessageBox.Show("Cannot convert bad int");
    return;
}

foreach(int i in wallet)
  //kaboom!

我们都没有收到正确的错误消息,也没有return退出该功能。

5)我不仅没有不正常,而且我也没有从错误中吸取教训

  

每次枚举时都会执行Linq。所以不要重复使用Linq enumerables。

假设您从Linq表达式返回IQueryableIEnumerable。现在枚举集合将获得执行语句,但只执行一次?不,每次都这样做。这在过去曾让我感到困惑。如果你有:

var p = listOfAMillionComplexItems.OrderBy(x => x.ComplexProperty);
MessageBox.Show(p.Count().ToString()); //long process.
MessageBox.Show(p.Count().ToString()); //long process still.

做得更好

int i = p.Count(); //store in a variable to access count
//or better
var list = p.ToList(); //and start using list

6)如果您不知道如何使用我,我可能会产生副作用!

  

与上述相同,只是为了说明如何重用Linq枚举可能导致不良行为。

确保你不进行副作用编程(因为在Linq中重新枚举更为常见)举个例子,

p = bag.Select((t, i) => {if(i == 1) MessageBox.Show("Started off"); return t;});

如果你列举两次,就会知道会发生什么不良事件。

7)警惕我在执行链接时执行

  

不仅仅是变量,即使链接的Linq函数也可以按照您通常的预期顺序执行(尽管行为是正确的)。不要想必要(一步一步),想想Linq怎么可能执行它。

例如,

var d = Enumerable.Range(1, 100);
var f = d.Select(t => new Person());
f = f.Concat(f);
f.Distinct().Count() ??

f中不同人物的数量是多少?我猜100,不,但它是200.问题在于,当实际执行连接逻辑时,f仍然是d.Select(t => new Person() 未执行。所以这有效地产生了

f = d.Select(t => new Person()).Concat(d.Select(t => new Person()));

然后有200个不同的成员。 Here's a link for the actual problem

8)嘿,实际上我们比你想象的更聪明。

  

本身并不是一个警告,但在很多情况下,Linq可以胜过你的命令式风格程序。所以在优化之前,再考虑一下,甚至是基准。

延迟执行基本上按需执行的原因使Linq比它看起来更有效率。迭代器块&#34;产生&#34;按照要求,一次一个项目,在不再需要时提供停止执行的能力。这是一个非常好的问题,详细说明:Order of LINQ extension methods does not affect performance?

9)我不打算收紧数字

  

滥用Linq会使代码效率低下,而且可读性也会降低。

对于数字运算算法,Linq不是正确的工具,特别是对于复杂度可以指数级扩展的大型数据集。有时只需要两个for循环就足够了。与LINQ to SQL相比,这同样适用于原始SQL。

10)雇用我做正确的工作

  

要求Linq注意您的正常业务是糟糕的编程选择,这与可读性相悖。

有些例如:

medicines.Any(p =>
{
    Console.WriteLine(p);
    return false;
});

对于可枚举的foreach。

medicines = medicines.Select(p =>
{
    p.Id = 3;
    return p;
});

只是糟糕的工具。

11)调试和分析可能是一场噩梦

  

很难跟随VS的Linq表达式发生的事情。

这不是完全不可能的,但是调试linq查询的任务与VS本身的非linq代码一样有效。由于延迟执行的性质,分析也变得更难。但是它不应该阻止任何人做一些简单的一两个衬垫!


一些警告或多或少都与延迟执行有关! A ditto question here。关于SO的一些相关阅读:

Examples on when not to use LINQ

Pros and Cons of LINQ (Language-Integrated Query)

What is the biggest mistake people make when starting to use LINQ?

drawbacks of linq