常见的Linq /标准查询运算符错误/错误步骤?

时间:2010-09-13 19:27:32

标签: c# .net linq

对于那些不是来自函数式编程背景的程序员,是否有错误要避免?

4 个答案:

答案 0 :(得分:11)

人们倾向于犯的最大错误是误解了LINQ查询的懒惰和评估规则:

查询是懒惰的:在迭代它们之前它们不会被执行:

// This does nothing! No query executed!
var matches = results.Where(i => i.Foo == 42);

// Iterating them will actually do the query.
foreach (var match in matches) { ... }

此外,结果不会被缓存。每次迭代它们时都会计算它们:

var matches = results.Where(i => i.ExpensiveOperation() == true);

// This will perform ExpensiveOperation on each element.
foreach (var match in matches) { ... }

// This will perform ExpensiveOperation on each element again!
foreach (var match in matches) { ... }

底线:知道您的查询何时执行。

答案 1 :(得分:4)

  

对于那些不是来自函数式编程背景的程序员,是否有错误要避免?

好问题。正如Judah所指出的,最大的一个是查询表达式构造查询,它不会执行它构造的查询。

这个事实的直接后果是两次执行相同的查询会返回不同的结果。

该事实的直接后果是第二次执行查询时不会重复使用先前执行的结果,因为新结果可能不同。

另一个重要的事实是查询最擅长提问而不是改变状态。尽量避免任何直接或间接导致某些内容改变其值的查询。例如,很多人尝试做类似的事情:

int number; 
from s in strings let b = Int32.TryParse(s, out number) blah blah blah

这只是要求一个痛苦的世界,因为TryParse会改变查询之外的变量的值。

在特定情况下,你最好做

int? MyParse(string s) 
{ 
    int result;
    return Int32.TryParse(s, out result) ? (int?)result : (int?)null;
}
...
from s in strings let number = MyParse(s) where number != null blah blah blah...

答案 2 :(得分:3)

IMO当你面对LINQ时,你必须知道这些主题(它们是错误的重要来源):

Deferred Execution (on SO)

Closure (on SO - 1)

Closure (on SO - 2)

Closure (Eric Lippert's Blog)

答案 3 :(得分:3)

了解closures的语义。

虽然这不仅仅局限于LINQ查询,但在LINQ中关闭变量确实会更频繁地出现,因为它是使用lambda表达式的最常见的地方之一。

虽然闭包非常有用,但它们也会让人感到困惑并导致错误的代码错误。闭包是“活的”(意味着对捕获的表达式之外的变量的更改对于表达式是可见的)这一事实对于一些开发人员来说也是意外的。

以下是闭包为LINQ查询创建问题的示例。在这里,闭包和延迟执行的结合使用会产生不正确的结果:

// set the customer ID and define the first query
int customerID = 12345;
var productsForFirstCustomer = from o in Orders
                               where o.CustomerID = customerID
                               select o.ProductID;

// change customer ID and compose another query...
customerID = 56789; // change the customer ID...
var productsForSecondCustomer = from o in Orders
                                where o.CustomerID = customerID
                                select o.ProductID;

if( productsForFirstCustomer.Any( productsForSecondCustomer ) )
{
   // ... this code will always execute due to the error above ...
}

此查询将始终输入if() { }语句的主体,因为通过更改customerID值会影响两个查询的执行 - 事实上,它们都使用相同的ID,因为customerID在两个LINQ语句中都捕获了变量