常见的Linq /标准查询运算符错误/错误步骤?
对于不是来自函数式编程背景的程序员,是否有错误要避免?
人们倾向于犯的最大错误是误解了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) { ... }
底线:知道您的查询何时执行。
对于不是来自函数式编程背景的程序员,是否有错误要避免?
好问题。 正如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...
IMO当你面对LINQ时,你必须知道这些主题(它们是错误的重要来源):
延期执行(在SO上)
关闭(在SO – 1上)
关闭(在SO – 2上)
关闭(Eric Lippert的博客)
理解闭包的语义。
虽然这不仅仅局限于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,因为在两个LINQ语句中都捕获了customerID
变量。