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" 这取决于您想要的输出是什么:

  1. 如果您想知道您是否有与谓词匹配的项目,那么:

    var result = list.Any(CheckName);
    
  2. 如果要检索与谓词匹配的那些:

    var result = list.Where(CheckName);
    

    如果稍后您想查询并检查 results 是否包含任何内容,则:

    if(result.Any()) { /* ... */ }
    

    如果您只想要结果(从而实现查询):

    list.Where(CheckName).ToList();
    

在此处阅读有关延迟执行的 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> 被实际访问后才发生。也就是所谓的延迟执行。