FirstOrDefault: Nilai default selain nol


142

Seperti yang saya pahami, dalam Linq metode FirstOrDefault()dapat mengembalikan Defaultnilai selain null. Apa yang saya belum berhasil adalah hal-hal apa selain null dapat dikembalikan dengan metode ini (dan serupa) ketika tidak ada item dalam hasil permintaan. Apakah ada cara tertentu yang dapat diatur sehingga jika tidak ada nilai untuk permintaan tertentu beberapa nilai yang telah ditentukan dikembalikan sebagai nilai default?


147
Alih-alih YourCollection.FirstOrDefault(), Anda bisa menggunakan YourCollection.DefaultIfEmpty(YourDefault).First()misalnya.
kemalasan

6
Saya sudah mencari sesuatu seperti komentar di atas cukup lama, itu sangat membantu. Ini harus menjadi jawaban yang diterima.
Brandon

Komentar di atas adalah jawaban terbaik.
Tom Padilla

Dalam kasus saya @sloth, jawaban tidak berfungsi ketika nilai yang dikembalikan adalah nol dan ditugaskan ke non nullable. Saya menggunakan MyCollection.Last().GetValueOrDefault(0)untuk itu. Kalau tidak, @Jon Skeet jawaban di bawah ini adalah IMO benar.
Jnr

Jawaban:


46

Kasus umum, bukan hanya untuk tipe nilai:

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

Sekali lagi, ini tidak bisa benar-benar tahu jika ada adalah sesuatu dalam urutan Anda, atau jika nilai pertama adalah default.

Jika Anda peduli tentang ini, Anda bisa melakukan sesuatu seperti

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

dan gunakan sebagai

var result = query.FirstOr(otherDefaultValue);

meskipun seperti yang ditunjukkan oleh Mr. Steak, ini bisa dilakukan juga oleh .DefaultIfEmpty(...).First().


Metode generik Anda perlu <T>dalam nama mereka, tetapi yang lebih serius adalah itu value == default(T)tidak berhasil (karena siapa yang tahu jika Tdapat dibandingkan untuk kesetaraan?)
AakashM

Terima kasih telah menunjukkannya, @AashashM; Saya sebenarnya sudah mencoba ini sekarang dan saya pikir itu harus OK (walaupun saya tidak suka tinju untuk jenis nilai).
Rawling

3
@Rawling Gunakan EqualityComparer<T>.Default.Equals(value, default(T))untuk menghindari tinju dan menghindari pengecualian jika nilainyanull
Lukazoid

199

Seperti yang saya pahami, di Linq metode FirstOrDefault () dapat mengembalikan nilai Default sesuatu selain null.

Tidak. Atau lebih tepatnya, selalu mengembalikan nilai default untuk jenis elemen ... yang merupakan referensi nol, nilai nol dari jenis nilai yang dapat dibatalkan, atau nilai "semua nol" yang alami untuk jenis nilai yang tidak dapat dibatalkan.

Apakah ada cara tertentu yang dapat diatur sehingga jika tidak ada nilai untuk permintaan tertentu beberapa nilai yang telah ditentukan dikembalikan sebagai nilai default?

Untuk jenis referensi, Anda bisa menggunakan:

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

Tentu saja ini juga akan memberi Anda "nilai default lain" jika nilai pertama ada, tetapi merupakan referensi nol ...


Saya tahu pertanyaannya menanyakan jenis referensi tetapi solusi Anda tidak berfungsi ketika elemen adalah tipe nilai int. Saya lebih memilih penggunaan DefaultIfEmpty: src.Where(filter).DefaultIfEmpty(defaultValue).First(). Berfungsi untuk tipe nilai dan tipe referensi.
KFL

@ KFL: Untuk tipe nilai yang tidak dapat dibatalkan, saya mungkin akan menggunakannya juga - tapi ini lebih panjang untuk kasus yang dapat dibatalkan.
Jon Skeet

Kontrol luar biasa atas jenis pengembalian saat defaultnya adalah nol .. :)
Sundara Prabu

"Tidak. Atau lebih tepatnya, selalu mengembalikan nilai default untuk tipe elemen ..." - Ini melakukannya untukku, sebenarnya .. karena aku juga salah memahami arti nama fungsi dengan mengasumsikan kamu dapat memberikan nilai default kapan pun diperlukan
Jesus Campon

Saya sangat menyukai pendekatan ini, tetapi saya memang mengubah "T alternate" dengan "Func <T> alternate", dan kemudian "return alternate ();" dengan begitu saya tidak menghasilkan objek tambahan kecuali saya perlu. Sangat berguna jika fungsi ini dipanggil berkali-kali berturut-turut, konstruktornya lambat atau instance alternatif dari tipe tersebut membutuhkan banyak memori.
Dan Violet Sagmiller

64

Anda dapat menggunakan DefaultIfEmpty diikuti oleh Pertama :

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

Saya suka ide DefaultIfEmpty- bekerja dengan semua API yang perlu default yang akan ditentukan: First(), Last(), dll Sebagai pengguna, Anda tidak perlu mengingat yang API memungkinkan untuk menentukan standar yang tidak. Sangat elegan!
KFL

Ini sangat banyak jawaban sarang.
Jesse Williams

19

Dari dokumentasi untuk FirstOrDefault

[Pengembalian] default (TSource) jika sumber kosong;

Dari dokumentasi untuk default (T) :

kata kunci default, yang akan mengembalikan nol untuk jenis referensi dan nol untuk jenis nilai numerik. Untuk struct, ini akan mengembalikan setiap anggota struct yang diinisialisasi ke nol atau nol tergantung pada apakah mereka tipe nilai atau referensi. Untuk tipe nilai nullable, default mengembalikan System.Nullable, yang diinisialisasi seperti struct apa pun.

Oleh karena itu, nilai default bisa nol atau 0 tergantung pada apakah tipe tersebut adalah tipe referensi atau nilai, tetapi Anda tidak dapat mengontrol perilaku default.


6

Disalin dari komentar oleh @sloth

Alih-alih YourCollection.FirstOrDefault(), Anda bisa menggunakan YourCollection.DefaultIfEmpty(YourDefault).First()misalnya.

Contoh:

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
Perhatikan bahwa DefaultIfEmptymengembalikan default JIKA koleksi kosong (memiliki 0 item). Jika Anda menggunakan FirstDENGAN ekspresi yang cocok seperti dalam contoh Anda dan kondisi itu tidak menemukan item apa pun nilai pengembalian Anda akan kosong.
OriolBG

5

Anda juga bisa melakukan ini

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

Ini hanya menggunakan linq - yipee!


2
Satu-satunya perbedaan antara jawaban ini dan jawaban Vitamin C adalah yang ini digunakan FirstOrDefaultsebagai ganti First. Menurut msdn.microsoft.com/en-us/library/bb340482.aspx , penggunaan yang disarankan adalahFirst
Daniel

5

Sebenarnya, saya menggunakan dua pendekatan untuk menghindari NullReferenceExceptionketika saya bekerja dengan koleksi:

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

Untuk C # 6.0 atau yang lebih baru:

Gunakan ?.atau ?[untuk menguji apakah nol sebelum melakukan akses anggota, dokumentasi Operator Null-kondisional

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

C # versi yang lebih lama:

Gunakan DefaultIfEmpty()untuk mengambil nilai default jika urutannya kosong. Dokumentasi MSDN

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


1
Operator propagasi nol tidak diizinkan di pohon ekspresi lamba.
Lars335

1

Alih-alih YourCollection.FirstOrDefault(), Anda bisa menggunakan YourCollection.DefaultIfEmpty(YourDefault).First()misalnya.


Ketika Anda berpikir bahwa itu membantu, Anda dapat mengungguli. Ini bukan jawaban.
jannagy02

1

Saya baru saja mengalami situasi yang sama dan sedang mencari solusi yang memungkinkan saya untuk mengembalikan nilai default alternatif tanpa mengurusnya di sisi pemanggil setiap kali saya membutuhkannya. Apa yang biasanya kita lakukan seandainya Linq tidak mendukung apa yang kita inginkan, adalah menulis ekstensi baru yang menanganinya. Itu yang saya lakukan. Inilah yang saya buat (belum diuji):

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

Saya tahu ini sudah lama tetapi saya akan menambahkan ini, berdasarkan jawaban yang paling populer tetapi dengan sedikit ekstensi ID ingin berbagi di bawah ini:

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

Ini memungkinkan saya untuk menyebutnya sebaris dengan contoh saya sendiri. Saya mengalami masalah dengan:

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

Jadi bagi saya, saya hanya ingin resolver default untuk digunakan inline, saya bisa melakukan pemeriksaan biasa dan kemudian meneruskan fungsi sehingga kelas tidak dipakai bahkan jika tidak digunakan, itu adalah fungsi untuk mengeksekusi ketika diperlukan sebagai gantinya!


Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.