FirstOrDefault:除 null 以外的默认值

Posted

技术标签:

【中文标题】FirstOrDefault:除 null 以外的默认值【英文标题】:FirstOrDefault: Default value other than null 【发布时间】:2012-10-19 10:25:35 【问题描述】:

据我了解,在 Linq 中,FirstOrDefault() 方法可以返回除 null 之外的 Default 值。我没有弄清楚的是,当查询结果中没有项目时,这个(和类似的)方法可以返回除了 null 之外的什么东西。有没有什么特别的方法可以设置,如果没有特定查询的值,一些预定义的值会作为默认值返回?

【问题讨论】:

例如,您可以使用YourCollection.DefaultIfEmpty(YourDefault).First(),而不是YourCollection.FirstOrDefault() 我一直在寻找类似上述评论的东西,它帮助很大。这应该是公认的答案。 以上评论是最佳答案。 在我的情况下,当返回的值可以为空并分配给不可为空的值时,@sloth 答案不起作用。我为此使用了MyCollection.Last().GetValueOrDefault(0)。否则@Jon Skeet 下面的答案是 IMO 正确的。 【参考方案1】:

据我了解,在 Linq 中,FirstOrDefault() 方法可以返回非 null 的默认值。

没有。或者更确切地说,它总是返回元素类型的默认值...它可以是空引用、可空值类型的空值,或者是非空值类型的自然“全零”值- 可空值类型。

有没有什么特别的方法可以设置,如果没有特定查询的值,一些预定义的值会作为默认值返回?

对于引用类型,您可以使用:

var result = query.FirstOrDefault() ?? otherDefaultValue;

当然,如果第一个值存在,这会给你“其他默认值”,但它是一个空引用......

【讨论】:

我知道这个问题要求引用类型,但是当元素是像 int 这样的值类型时,您的解决方案不起作用。我更喜欢使用DefaultIfEmptysrc.Where(filter).DefaultIfEmpty(defaultValue).First()。适用于值类型和引用类型。 @KFL:对于不可为空的值类型,我可能也会使用它——但对于可空的情况,它更冗长。 当默认值为 null 时对返回类型的出色控制 .. :) “不。或者更确切地说,它总是返回元素类型的默认值......” - 这实际上是为我做的......因为我也通过假设误解了函数名称的含义您可以在需要时提供任何默认值 我真的很喜欢这种方法,但我确实将“T alternate”更改为“Func alternate”,然后“return alternate();”这样我就不会生成额外的对象,除非我需要。如果函数被连续多次调用,构造函数很慢或者类型的替代实例占用大量内存,则特别有用。【参考方案2】:

您可以使用DefaultIfEmpty 后跟First:

T customDefault = ...;
IEnumerable<T> mySequence = ...;
mySequence.DefaultIfEmpty(customDefault).First();

【讨论】:

我喜欢 DefaultIfEmpty 的想法 - 它适用于所有需要指定默认值的 API:First()Last() 等。作为用户,您不需要记住哪些 API 允许指定默认值,哪些不允许。非常优雅! 这是非常老套的答案。【参考方案3】:

一般情况,不仅适用于值类型:

static class ExtensionsThatWillAppearOnEverything

    public static T IfDefaultGiveMe<T>(this T value, T alternate)
    
        if (value.Equals(default(T))) return alternate;
        return value;
    


var result = query.FirstOrDefault().IfDefaultGiveMe(otherDefaultValue);

同样,这并不能真正判断您的序列中是否有 任何内容,或者第一个值是否是默认值。

如果你关心这个,你可以做类似的事情

static class ExtensionsThatWillAppearOnIEnumerables

    public static T FirstOr<T>(this IEnumerable<T> source, T alternate)
    
        foreach(T t in source)
            return t;
        return alternate;
    

并用作

var result = query.FirstOr(otherDefaultValue);

尽管 Steak 先生指出,.DefaultIfEmpty(...).First() 也可以做到这一点。

【讨论】:

你的泛型方法的名字中需要&lt;T&gt;,但更严重的是value == default(T)不起作用(因为谁知道T是否可以比较相等?) 感谢您指出这一点,@AakashM;我现在实际上已经尝试过了,我认为应该没问题(尽管我不喜欢值类型的装箱)。 @Rawling 使用EqualityComparer&lt;T&gt;.Default.Equals(value, default(T)) 来避免装箱并在值为null 时避免异常【参考方案4】:

来自FirstOrDefault的文档

如果源为空,则返回默认值(TSource);

来自default(T)的文档:

默认关键字,对于引用类型将返回 null,对于数值类型将返回零。对于结构,它将返回结构的每个成员,根据它们是值类型还是引用类型,初始化为零或空。对于可为空的值类型,默认返回一个 System.Nullable,它像任何结构一样被初始化。

因此,根据类型是引用类型还是值类型,默认值可以为 null 或 0,但您无法控制默认行为。

【讨论】:

【参考方案5】:

从@sloth 的评论中复制过来

例如,您可以使用YourCollection.DefaultIfEmpty(YourDefault).First(),而不是YourCollection.FirstOrDefault()

例子:

var viewModel = new CustomerDetailsViewModel
    
            MainResidenceAddressSection = (MainResidenceAddressSection)addresses.DefaultIfEmpty(new MainResidenceAddressSection()).FirstOrDefault( o => o is MainResidenceAddressSection),
            RiskAddressSection = addresses.DefaultIfEmpty(new RiskAddressSection()).FirstOrDefault(o => !(o is MainResidenceAddressSection)),
    ;

【讨论】:

请注意,DefaultIfEmpty 在集合为空(有 0 个项目)时返回默认值。如果您使用First WITH 与示例中的匹配表达式,并且该条件找不到任何项目,您的返回值将为空。【参考方案6】:

你也可以这样做

    Band[] objects =  new Band  Name = "Iron Maiden"  ;
    first = objects.Where(o => o.Name == "Slayer")
        .DefaultIfEmpty(new Band  Name = "Black Sabbath" )
        .FirstOrDefault();   // returns "Black Sabbath" 

这仅使用 linq - yipee!

【讨论】:

这个答案和维生素 C 的答案的唯一区别是这个答案使用FirstOrDefault 而不是First。根据msdn.microsoft.com/en-us/library/bb340482.aspx,推荐的用法是First【参考方案7】:

实际上,当我使用集合时,我使用两种方法来避免NullReferenceException

public class Foo

    public string Barget; set;

void Main()

    var list = new List<Foo>();
    //before C# 6.0
    string barCSharp5 = list.DefaultIfEmpty(new Foo()).FirstOrDefault().Bar;
    //C# 6.0 or later
    var barCSharp6 = list.FirstOrDefault()?.Bar;

对于 C# 6.0 或更高版本:

在执行成员访问Null-conditional Operators documentation之前使用?.?[测试是否为空

示例: var barCSharp6 = list.FirstOrDefault()?.Bar;

C# 旧版本:

如果序列为空,则使用DefaultIfEmpty() 检索默认值。MSDN Documentation

示例: string barCSharp5 = list.DefaultIfEmpty(new Foo()).FirstOrDefault().Bar;

【讨论】:

表达式树lamba中不允许空传播运算符。【参考方案8】:

例如,您可以使用YourCollection.DefaultIfEmpty(YourDefault).First(),而不是YourCollection.FirstOrDefault()

【讨论】:

当您认为它有帮助时,您可以投票。这不是答案。【参考方案9】:

我刚刚遇到了类似的情况,正在寻找一种解决方案,该解决方案允许我返回一个替代默认值,而无需在每次需要时都在调用方处理它。如果 Linq 不支持我们想要的,我们通常会做的是编写一个新的扩展来处理它。这就是我所做的。这是我想出的(虽然未经测试):

public static class EnumerableExtensions

    public static T FirstOrDefault<T>(this IEnumerable<T> items, T defaultValue)
    
        foreach (var item in items)
        
            return item;
        
        return defaultValue;
    

    public static T FirstOrDefault<T>(this IEnumerable<T> items, Func<T, bool> predicate, T defaultValue)
    
        return items.Where(predicate).FirstOrDefault(defaultValue);
    

    public static T LastOrDefault<T>(this IEnumerable<T> items, T defaultValue)
    
        return items.Reverse().FirstOrDefault(defaultValue);
    

    public static T LastOrDefault<T>(this IEnumerable<T> items, Func<T, bool> predicate, T defaultValue)
    
        return items.Where(predicate).LastOrDefault(defaultValue);
    

【讨论】:

【参考方案10】:

这对我们有用

int valueOrDefault = myList.Find(x => x.Id == 1)?.Value ?? -1;

【讨论】:

【参考方案11】:

.NET6 / c#10 解决方案

.NET6 / c#10 通过向 *OrDefault LINQ 方法添加新功能解决了这个问题。新的重载允许您指定在序列为空时使用的默认值。

public static TSource FirstOrDefault<TSource> (this System.Collections.Generic.IEnumerable<TSource> source, Func<TSource,bool> predicate, TSource defaultValue);

返回序列中满足条件的第一个元素,如果没有找到这样的元素,则返回指定的默认值。

这是一个例子;

var nums = new List<int>  1, 2, 3 ;
var target = 4;

var value = nums.FirstOrDefault(x => x == target, -1); // value becomes -1.

来源:https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.firstordefault?view=net-6.0#System_Linq_Enumerable_FirstOrDefault__1_System_Collections_Generic_IEnumerable___0____0_

【讨论】:

【参考方案12】:

我知道它已经有一段时间了,但我会根据最流行的答案添加这个,但有一点扩展我想分享以下内容:

static class ExtensionsThatWillAppearOnIEnumerables

    public static T FirstOr<T>(this IEnumerable<T> source, Func<T, bool> predicate, Func<T> alternate)
    
        var thing = source.FirstOrDefault(predicate);
        if (thing != null)
            return thing;
        return alternate();
    

这使我可以在我遇到问题的自己的示例中将其称为内联:

_controlDataResolvers.FirstOr(x => x.AppliesTo(item.Key), () => newDefaultResolver()).GetDataAsync(conn, item.ToList())

所以对我来说,我只想内联使用默认解析器,我可以进行常规检查,然后传入一个函数,这样即使未使用类也不会实例化,而是在需要时执行的函数!

【讨论】:

【参考方案13】:

如果项目列表不是可空类型,请考虑强制转换为可空类型,然后将 firstordefault 和空合并运算符与您想要的任何默认值进行合并。

例子:

static class MyClass

    public enum Level  Low, Medium, High 

    public static void Dosomething()
    
        var levels = Enum.GetValues<Level>();

        var myLevel = levels.Select(x => (Level?)x).FirstOrDefault() ?? Level.Medium; // The Default value other than null
    

【讨论】:

【参考方案14】:

使用DefaultIfEmpty() 而不是FirstOrDefault()

【讨论】:

以上是关于FirstOrDefault:除 null 以外的默认值的主要内容,如果未能解决你的问题,请参考以下文章

我如何处理 .FirstOrDefault 方法?

js如何判断类型?

foreach + break vs linq FirstOrDefault 性能差异

LeetCode238. 除自身以外数组的乘积

实体框架4单()与第()VS FirstOrDefault()

除自身以外数组的乘积