我需要为列表中的每个项目调用该方法。因此,我按如下方式使用了Where<>
查询,
List<string> list = new List<string>();
list.Add("Name1");
list.Add("Name2");
list.Add("Name3");
var name = list.Where(n =>
{
return CheckName(n);
});
但在上述情况下,CheckName()
没有被击中。如果我使用FirstOrDefault<>
,则会触发相同的方法。我不知道这是框架中断还是我走错了路。
作为附加信息,我 using.NET 框架 4.5。
有没有人遇到过这个错误?如果是这样,是否有任何解决方案可以解决此问题?
您错误地理解了Where
条件的结果。由于 linq 被延迟执行,它只会在具体化时进入 where 条件(通过ToList
/FirstOrDefault
/Sum
等(。
Where
永远不会在当前代码中实际实现(它与您在使用FirstOrDefault
时所经历的那样(,因此它永远不会进入CheckName
方法。然后,由于Where
永远不会返回null
而是"最坏情况"一个空集合,这是不null
的,结果是true
。
如果调试,您将看到name
等于true
。要"克服"这一点,这取决于您想要的输出:
-
如果您想知道是否有任何与谓词匹配的项目,那么:
var result = list.Any(CheckName);
-
如果要检索与谓词匹配的那些:
var result = list.Where(CheckName);
如果稍后您想查询并检查
results
是否包含任何内容,则:if(result.Any()) { /* ... */ }
如果您只需要结果(从而具体化查询(:
list.Where(CheckName).ToList();
在此处阅读有关 linq 被延迟执行的更多信息:
- 林克和延迟执行
- 在 LINQ 中使用延迟执行有什么好处?
作为旁注,请参阅如何从以下位置更改当前代码:
var name = list.Where(n =>
{
return CheckName(n);
})
自:
var name = list.Where(n => CheckName(n));
并最终:
var name = list.Where(CheckName);
LINQ 有一个延迟执行主体,这意味着除非您访问name
变量,否则不会执行查询。如果你想立即执行它,(例如(最后添加.ToList()
,这正是FirstOrDefault
所做的。它执行立即执行而不是延迟执行。
var name = list.Where(n =>
{
return CheckName(n);
}).ToList() != null;
此外where
条件结果永远不会null
。即使list
中没有对象满足CheckName
中的条件,where
也会返回一个空集合。
由于Linq 的延迟执行,未执行CheckName()
方法。实际语句在您实际访问它之前不会执行。所以在你的情况下,对于CheckName()
,你应该做这样的事情:
var name = list.Where(n =>
{
return CheckName(n);
}).ToList();
当您查看Where
-Method 源代码时,您可以轻松了解原因:
internal static IEnumerable<T> Where<T>(this IEnumerable<T> enumerable, Func<T, bool> where) {
foreach (T t in enumerable) {
if (where(t)) {
yield return t;
}
}
}
收益率将导致仅在实际访问返回的IEnumerable<T>
后执行。这就是所谓的延迟执行。
如果需要为列表中的每个项目调用方法,则应使用简单的 for 循环:
foreach var name in list
CheckName(name);
仅仅因为 LINQ 可用,并不意味着应该在有集合的任何地方使用它。编写有意义的代码并且是自我注释的代码非常重要,在这里使用它同时会给您的逻辑引入缺陷,并使代码更难阅读、理解和维护。对于所述目的,这是错误的工具
毫无疑问,您还有其他未在此处说明的要求,例如"我想检查列表中的每个名称并确保没有一个为空"。你可以而且可能应该为此使用 linq,但它看起来更像
bool allNamesOK = list.All(n => n != null);
这段代码很紧凑,读起来很好;我们可以清楚地看到意图(虽然我不会称列表为"列表" - "名称"会更好(