Where<> 查询中的谓词未命中?
Predicate inside a Where<> query not hit?
我需要为列表中的每个项目调用该方法。所以,我使用了 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 Framework 4.5。
有没有人遇到过这个错误?如果是这样,有什么解决方案可以解决这个问题吗?
您错误地理解了 Where
条件的结果。由于 linq 被延迟执行,它只会在具体化时进入 where 条件(通过 ToList
/FirstOrDefault
/Sum
等)。
Where
从未真正在您当前的代码中具体化(它就像您在使用 FirstOrDefault
时所经历的那样),因此它永远不会进入 CheckName
方法。然后,由于 Where
永远不会 return null
而是 "worst case" 一个空集合,它不是 null
,结果是 true
.
如果你调试,你会看到 name
等于 true
最后。要 "overcome" 这取决于您想要的输出是什么:
如果您想知道您是否有与谓词匹配的项目,那么:
var result = list.Any(CheckName);
如果要检索与谓词匹配的那些:
var result = list.Where(CheckName);
如果稍后您想查询并检查 results
是否包含任何内容,则:
if(result.Any()) { /* ... */ }
如果您只想要结果(从而实现查询):
list.Where(CheckName).ToList();
在此处阅读有关延迟执行的 linq 的更多信息:
- Linq and deffered execution
- What are the benefits of a Deferred Execution in 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
也会 return 一个空集合。
CheckName()
方法未执行,因为 Linq 延迟执行。在您实际访问它之前,不会执行实际的语句。所以在你的情况下,对于 CheckName()
,你应该做这样的事情:
var name = list.Where(n =>
{
return CheckName(n);
}).ToList();
如果您需要为列表中的每个项目调用一个方法,那么您应该使用简单的 for 循环:
foreach var name in list
CheckName(name);
仅仅因为 LINQ 可用,并不意味着它应该在任何有集合的地方使用。重要的是编写有意义且自我注释的代码,在这里使用它会同时在您的逻辑中引入缺陷,并使您的代码更难阅读、理解和维护。这是用于所述目的的错误工具
毫无疑问,您还有此处未说明的其他要求,例如 "I want to check every name in a list and make sure that none are null"。您可以并且可能应该为此使用 linq,但它看起来更像
bool allNamesOK = list.All(n => n != null);
这段代码简洁易读;我们可以清楚地看到意图(虽然我不会称列表为 "list" - "names" 会更好)
当您查看 Where
-方法 source 代码时,您可以很容易地看出原因:
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;
}
}
}
yield 将导致执行仅在返回的 IEnumerable<T>
被实际访问后才发生。也就是所谓的延迟执行。
我需要为列表中的每个项目调用该方法。所以,我使用了 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 Framework 4.5。
有没有人遇到过这个错误?如果是这样,有什么解决方案可以解决这个问题吗?
您错误地理解了 Where
条件的结果。由于 linq 被延迟执行,它只会在具体化时进入 where 条件(通过 ToList
/FirstOrDefault
/Sum
等)。
Where
从未真正在您当前的代码中具体化(它就像您在使用 FirstOrDefault
时所经历的那样),因此它永远不会进入 CheckName
方法。然后,由于 Where
永远不会 return null
而是 "worst case" 一个空集合,它不是 null
,结果是 true
.
如果你调试,你会看到 name
等于 true
最后。要 "overcome" 这取决于您想要的输出是什么:
如果您想知道您是否有与谓词匹配的项目,那么:
var result = list.Any(CheckName);
如果要检索与谓词匹配的那些:
var result = list.Where(CheckName);
如果稍后您想查询并检查
results
是否包含任何内容,则:if(result.Any()) { /* ... */ }
如果您只想要结果(从而实现查询):
list.Where(CheckName).ToList();
在此处阅读有关延迟执行的 linq 的更多信息:
- Linq and deffered execution
- What are the benefits of a Deferred Execution in 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
也会 return 一个空集合。
CheckName()
方法未执行,因为 Linq 延迟执行。在您实际访问它之前,不会执行实际的语句。所以在你的情况下,对于 CheckName()
,你应该做这样的事情:
var name = list.Where(n =>
{
return CheckName(n);
}).ToList();
如果您需要为列表中的每个项目调用一个方法,那么您应该使用简单的 for 循环:
foreach var name in list
CheckName(name);
仅仅因为 LINQ 可用,并不意味着它应该在任何有集合的地方使用。重要的是编写有意义且自我注释的代码,在这里使用它会同时在您的逻辑中引入缺陷,并使您的代码更难阅读、理解和维护。这是用于所述目的的错误工具
毫无疑问,您还有此处未说明的其他要求,例如 "I want to check every name in a list and make sure that none are null"。您可以并且可能应该为此使用 linq,但它看起来更像
bool allNamesOK = list.All(n => n != null);
这段代码简洁易读;我们可以清楚地看到意图(虽然我不会称列表为 "list" - "names" 会更好)
当您查看 Where
-方法 source 代码时,您可以很容易地看出原因:
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;
}
}
}
yield 将导致执行仅在返回的 IEnumerable<T>
被实际访问后才发生。也就是所谓的延迟执行。