FirstOrDefault:非null的默认值


142

据我了解,在Linq中,该方法FirstOrDefault()可以返回Default非null值。我还没有解决的是,当查询结果中没有项目时,此(和类似方法)可以返回null以外的其他东西。有什么特殊的方法可以设置它,以便在没有特定查询的值的情况下,返回一些预定义值作为默​​认值?


147
YourCollection.FirstOrDefault()可以使用代替,YourCollection.DefaultIfEmpty(YourDefault).First()例如。
2012年

6
我一直在寻找像上面的评论这样的东西已经有一段时间了,它极大地帮助了我。这应该是公认的答案。
布兰登2014年

上面的评论是最好的答案。
汤姆·帕迪拉

在我的情况下,当返回的值可为空并且分配为不可为空时,@ sloth答案不起作用。我曾经MyCollection.Last().GetValueOrDefault(0)那样。否则,下面的@Jon Skeet答案是IMO正确的。
Jnr

Answers:


46

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

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()


您的通用方法需要<T>使用它们的名称,但是更严重的是,value == default(T)它不起作用(因为谁知道是否T可以将其等同进行比较?)
AakashM 2012年

感谢您指出,@ AakashM; 我实际上已经尝试过了,我认为应该没问题(尽管我不喜欢值类型的拳击)。
罗林

3
@Rawling EqualityComparer<T>.Default.Equals(value, default(T))用于避免装箱并避免异常(如果值是null
Lukazoid

199

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

否。或者,它总是返回元素类型的默认值...它可以是空引用,可为空的值类型的空值,或为非可为空的值类型的自然“全零”值。

有什么特殊的方法可以设置它,以便在没有特定查询的值的情况下,返回一些预定义值作为默​​认值?

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

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

这当然给你“等默认值”如果第一个值存在,但为空引用...


我知道这个问题要求引用类型,但是当元素为时,您的解决方案将无法使用int。我更喜欢使用DefaultIfEmptysrc.Where(filter).DefaultIfEmpty(defaultValue).First()。适用于值类型和引用类型。
自由联盟

@KFL:对于非可空值类型,我也可能会用到它-但是对于可空情况更费时。
乔恩·斯基特

默认值为null时,对返回类型的控制非常好.. :)
Sundara Prabu

“不是。或者,它总是返回元素类型的默认值...”-实际上是为我做的。因为我也通过假设您可以在需要时提供任何默认值,而误解了函数名称的含义。
耶稣·坎普,

我真的很喜欢这种方法,但是我确实将“ Tternate”更改为“ Func <T> alternate”,然后“ return alter();”。这样,除非需要,否则我不会生成额外的对象。如果该函数连续被调用多次,构造函数很慢或该类型的备用实例占用大量内存,则特别有用。
Dan Violet Sagmiller

64

您可以使用DefaultIfEmpty后跟First

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

我喜欢这个想法DefaultIfEmpty-它可与所有需要指定默认值的API配合使用First(),例如Last(),等。作为用户,您无需记住允许指定默认值的API。十分优雅!
KFL

这很大程度上就是答案。
杰西·威廉姆斯

19

FirstOrDefault的文档中

[返回] default(TSource)如果source为空;

default(T)的文档中:

默认关键字,对于引用类型,它将返回null;对于数值类型,它将返回零。对于结构,它将返回初始化为零或null的结构的每个成员,具体取决于它们是值类型还是引用类型。对于可为空的值类型,默认返回System.Nullable,该系统将像任何结构一样进行初始化。

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


6

从@sloth的评论中复制过来

YourCollection.FirstOrDefault()可以使用代替,YourCollection.DefaultIfEmpty(YourDefault).First()例如。

例:

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)),
    };

2
请注意,DefaultIfEmpty如果集合为空(具有0个项目),则返回默认值。如果First在示例中使用WITH匹配表达式,并且该条件找不到任何项目,则返回值为空。
OriolBG'3

5

您也可以这样做

    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!


2
此答案与维生素C答案之间的唯一区别是,此答案使用FirstOrDefault而不是First。根据msdn.microsoft.com/zh-cn/library/bb340482.aspx,建议的用法是First
Daniel Daniel

5

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

public class Foo
{
    public string Bar{get; 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文档

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

C#较旧的版本:

使用DefaultIfEmpty()如果序列是空检索默认值。MSDN文档

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


1
表达式树lamba中不允许使用空传播运算符。
Lars335

1

YourCollection.FirstOrDefault()可以使用代替,YourCollection.DefaultIfEmpty(YourDefault).First()例如。


如果您认为有帮助,可以投票。这不是答案。
jannagy02年

1

我刚刚遇到过类似的情况,并且正在寻找一种解决方案,该解决方案使我可以返回备用默认值,而不必在每次需要时都在调用方使用它。如果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);
    }
}

0

我知道已经有一段时间了,但根据最流行的答案,我会加一句:我想分享以下内容:

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())

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


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.