FirstOrDefault:null以外のデフォルト値


142

私が理解しているように、Linqでは、メソッドFirstOrDefault()Defaultnull以外の値を返すことができます。クエリの結果にアイテムがない場合、この(および同様の)メソッドでnull以外のものが返されることはわかりません。これを設定して、特定のクエリに値がない場合に事前定義された値がデフォルト値として返されるようにするための特別な方法はありますか?


147
の代わりにYourCollection.FirstOrDefault()YourCollection.DefaultIfEmpty(YourDefault).First()たとえば使用できます。
ナマケモノ

6
上記のコメントのようなものをずっと探していましたが、それは非常に役に立ちました。これは受け入れられる答えになるはずです。
ブランドン

上記のコメントが最良の答えです。
Tom Padilla、2014年

私の場合、返された値がnull可能でnull以外に割り当てられている場合、@ slothの回答は機能しませんでした。私MyCollection.Last().GetValueOrDefault(0)はそのために使用しました。そうでない場合、以下の@Jon Skeetの回答はIMOで正しいです。
Jnr

回答:


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

ステーキ氏が指摘するように、これは同様に行うことができます.DefaultIfEmpty(...).First()


ジェネリックメソッド<T>には名前が必要ですが、もっと深刻なのはそれvalue == default(T)が機能しないことです(T同等かどうかを比較できるかどうかは誰が知っているためですか?)
AakashM

それを指摘してくれてありがとう、@ AakashM; 私は実際にこれを試しましたが、大丈夫だと思います(値の型のボクシングは好きではありませんが)。
Rawling

3
@Rawling EqualityComparer<T>.Default.Equals(value, default(T))ボクシングを回避し、値が次の場合の例外を回避するために使用null
Lukazoid

199

私が理解しているように、Linqでは、FirstOrDefault()メソッドはnull以外のデフォルト値を返すことができます。

いいえ。むしろ、常に要素タイプのデフォルト値を返します。これは、null参照、null許容値タイプのnull値、またはnull許容値タイプの自然な「すべてゼロ」値のいずれかです。

これを設定して、特定のクエリに値がない場合に事前定義された値がデフォルト値として返されるようにするための特別な方法はありますか?

参照型の場合は、次のものを使用できます。

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

もちろん、これは最初の値が存在する場合は「他のデフォルト値」提供しますが、null参照です...


質問が参照型を要求することは知っていますが、要素がのような値型である場合、ソリューションは機能しませんint。私はDefaultIfEmpty:の使用を非常に好みますsrc.Where(filter).DefaultIfEmpty(defaultValue).First()。値タイプと参照タイプの両方で機能します。
KFL 2018

@KFL:null可能ではない値の型の場合、私もおそらくそれを使用しますが、null可能の場合はより長くなります。
ジョンスキート2018

デフォルトがnullの場合の戻り値のタイプに対するすばらしい制御.. :)
Sundara Prabu 2018

「いいえ。それどころか、常に要素タイプのデフォルト値を返します...」-これは私にとって実際に行われました。実際に、必要なときにデフォルト値を提供できると想定して、関数名の意味を誤解したためです。
ジーザスカンポン

私はこのアプローチが本当に好きでしたが、「T代替」を「Func <T>代替」に変更してから、「return alternate();」に変更しました。そうすれば、必要がない限り、追加のオブジェクトを生成しません。関数が連続して何度も呼び出される場合、コンストラクターが遅い場合、または型の代替インスタンスが大量のメモリを消費する場合に特に役立ちます。
Dan Violet Sagmiller、

64

DefaultIfEmptyに続けてFirstを使用できます。

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

私はのアイデアを愛するDefaultIfEmpty-それはデフォルトを指定するために必要なすべてのAPIで動作しますFirst()Last()などのユーザーとして、あなたがないデフォルトを指定することが可能にするAPIを覚えておく必要はありません。とてもエレガント!
KFL 2018

これが非常に重要な答えです。
ジェシーウィリアムズ

19

FirstOrDefaultのドキュメントから

[戻り値]ソースが空の場合はdefault(TSource)。

default(T)のドキュメントから:

デフォルトのキーワード。参照タイプの場合はnullを返し、数値タイプの場合はゼロを返します。構造体の場合、それが値型か参照型かに応じて、ゼロまたはnullに初期化された構造体の各メンバーを返します。null可能な値の型の場合、defaultは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 2017年

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代わりに使用することですFirstmsdn.microsoft.com/en-us/library/bb340482.aspxによると、推奨される使用法はFirst
Daniel

5

実際、私はNullReferenceExceptionコレクションを操作するときに回避するために2つのアプローチを使用しています。

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かどうかをテストしますnull 条件演算子のドキュメント

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

C#の古いバージョン:

DefaultIfEmpty()シーケンスが空の場合にデフォルト値を取得するために使用します。MSDNドキュメント

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


1
null伝播演算子は、式ツリーのランバでは許可されていません。
Lars335 2017年

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

だから私はデフォルトのリゾルバーをインラインで使用したかったので、通常のチェックを行ってから、関数を渡して、未使用でもクラスがインスタンス化されないようにすることができます。代わりに、必要なときに実行する関数です!


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.