Lambda表达式 Where与FirstOrDefault

3

我是新手,正在学习使用lamda表达式,并尝试弄清楚一些事情。 我已经创建了以下代码部分,该代码返回日志文件的文件路径。

public static string GetLogFile()
{
    var fileTarget = LogManager.Configuration.AllTargets.Where(t=>t.Name == "LogName") as FileTarget;            
    return fileTarget == null ? string.Empty : fileTarget.FileName.Render(new LogEventInfo { Level = LogLevel.Info });
}

我的问题是当我使用以下代码时,fileTarget为空:

LogManager.Configuration.AllTargets.Where(t=>t.Name == "LogName")

但是,如果我将那一行代码更改为:
LogManager.Configuration.AllTargets.FirstOrDefault(t=>t.Name == "LogName")

返回我的日志文件的正确路径。有人能否解释一下WhereFirstOrDefault之间是否有重大区别?


4
是的,Where返回一个IEnumerable。当你试图用as进行类型强制转换时,它会返回null;但是FirstOrDefault返回一个可被强制转换的实例。 - Sebastian Hofmann
1
可以这样理解:Where 表示“给我所有 name == 'LogName' 的记录”,而 FirstOrDefault 则表示“给我第一条 name == 'LogName' 的记录,如果没有则返回 null”。 - Renatas M.
在进行类型转换时,使用value as <type>时要小心。如果您期望值是该类型,请使用(<type>)value - DavidG
3个回答

2
在您的情况下,Where 返回一个 FileTarget 对象列表,然后您将此列表转换为 FileTarget。这就是它为空的原因。
但是,FirstOrDefault 返回一个对象或 null,可以强制转换为您的类 FileTarget。这就是它能够工作的原因。

在您的情况下,Where 返回一个 IEnumerable,是否存在不成立的情况?另外,IEnumerable 不是列表。 - Camilo Terevinto
1
公平地说,IEnumerable 也不是一个列表。它只是一个承诺,可以迭代某些东西。例如,您可以将传入的 TCP 流公开为 IEnumerable,这显然不是一个列表,并且可能永远不会结束。这可能只是一些微小的语义问题,但相当关键。 - DavidG
@AntoineV 我也没有写 List,所以我不知道那个注释回答了什么。 - Camilo Terevinto

2

Where实际上返回一个IEnumerable(智能提示会告诉你这一点)。它不知道你的条件可能匹配多少项。FirstOrDefault将获取第一项,或者第一项与你的条件相匹配。如果你传了一个条件。

(注:谓词是任何接受对象并返回布尔值的函数,有一个名为“Predicate<T>”的.Net类型表示它)

要整理所有内容,你可以使用OfType linq操作符以及空值传播和空值合并运算符。

public static string GetLogFile()
{
    var fileTarget = LogManager.Configuration.AllTargets.OfType<FileTarget>().FirstOrDefault(t=>t.Name == "LogName");            
    return fileTarget?.FileName.Render(new LogEventInfo { Level = LogLevel.Info }) ?? string.Empty;
}

1
简单来说,Where 会提供一个可枚举对象。如果谓词没有匹配项,则不会为空而是空的。 FirstOrDefault 将提供一个对象。如果没有匹配项,则为 null。
根据您的代码,它应该抛出编译错误,指出无法将 IEnumerable 转换为 FileTarget。

不会抛出编译错误,因为 x as y 强制转换并不是这样工作的。 - DavidG

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接