FirstOrDefault: null dışında varsayılan değer


142

Anladığım kadarıyla, Linq'de yöntem null dışında bir şeyin değerini FirstOrDefault()döndürebilir Default. Ne ben işe yaramadı null dışında ne tür ne zaman sorgu sonuç hiçbir öğe olduğunda bu (ve benzer) yöntemle döndürülebilir olduğunu. Bunun belirli bir sorgu için değer yoksa, önceden tanımlanmış bazı değerlerin varsayılan değer olarak döndürülmesini sağlayacak özel bir yolu var mı?


147
Bunun yerine, örneğin YourCollection.FirstOrDefault()kullanabilirsiniz YourCollection.DefaultIfEmpty(YourDefault).First().
tembellik

6
Bir süredir yukarıdaki yorum gibi bir şey arıyordum, bu son derece yardımcı oldu. Bu kabul edilen cevap olmalı.
Brandon

Yukarıdaki yorum en iyi cevaptır.
Tom Padilla

Benim durumumda döndürülen değer nullable ve nullable atandığında @sloth cevap işe yaramadı. Bunun MyCollection.Last().GetValueOrDefault(0)için kullandım. Aksi takdirde, @Jon Skeet'in cevabı IMO doğru.
Jnr

Yanıtlar:


46

Genel değer, sadece değer türleri için değil:

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

Orada Yine, bu gerçekten söyleyemem oldu sıranızdaki şey, ya da ilk değer varsayılan olsaydı.

Eğer bunu önemsersen, böyle bir şey yapabilirsin

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

ve olarak kullan

var result = query.FirstOr(otherDefaultValue);

Her ne kadar Bay Steak'in işaret ettiği gibi, bu da yapılabilir .DefaultIfEmpty(...).First().


Jenerik yöntemlerinizin <T>isimlerine ihtiyacı vardır, ancak daha ciddi olanı value == default(T)işe yaramaz (çünkü Teşitlik için karşılaştırılıp karşılaştırılamayacağını kim bilebilir ?)
AakashM

Bunu işaret ettiğiniz için teşekkürler, @AakashM; Aslında bunu şimdi denedim ve değer türleri için boks sevmiyorum rağmen Tamam olması gerektiğini düşünüyorum.
Rawling

3
@Rawling Bokstan EqualityComparer<T>.Default.Equals(value, default(T))kaçınmak ve değer ise istisnadan kaçınmak için kullanınnull
Lukazoid

199

Anladığım kadarıyla, Linq'de FirstOrDefault () yöntemi null dışında bir şeyin Varsayılan değerini döndürebilir.

Hayır. Ya da, her zaman öğe türü için varsayılan değeri döndürür ... boş değer, boş değer türünün boş değeri ya da boş değer olmayan değer türü için doğal "tüm sıfırlar" değeri.

Bunun belirli bir sorgu için değer yoksa, önceden tanımlanmış bazı değerlerin varsayılan değer olarak döndürülmesini sağlayacak özel bir yolu var mı?

Referans türleri için şunları kullanabilirsiniz:

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

Tabii bu olacaktır da ilk değer mevcut olmakla birlikte, null başvuru ise size "diğer varsayılan değer" vermek ...


Sorunun referans türünü istediğini biliyorum, ancak elemanlar gibi değer türleri olduğunda çözümünüz işe yaramıyor int. Ben çok kullanılmasını tercih DefaultIfEmpty: src.Where(filter).DefaultIfEmpty(defaultValue).First(). Hem değer türü hem de referans türü için çalışır.
KFL

@KFL: Null edilemeyen değer türleri için muhtemelen bunu da kullanırım - ancak nullable vakalar için daha uzun soluklu.
Jon Skeet

Varsayılan boş olduğunda dönüş türleri üzerinde harika kontrol .. :)
Sundara Prabu 17:18

"Hayır. Ya da daha doğrusu, her zaman öğe türü için varsayılan değeri döndürür ..." - Bu benim için yaptım, aslında .. Ayrıca işlev adının anlamını yanlış anladığım için, gerektiğinde herhangi bir varsayılan değer sağlayabileceğinizi varsayarak
Jesus Campon

Bu yaklaşımı gerçekten çok beğendim, ancak "T alternate" i "Func <T> alternate" ile değiştirdim ve sonra "return alternate ();" bu şekilde ihtiyacım olmadıkça fazladan nesne üretmem. Özellikle işlev art arda birçok kez çağrılırsa, yapıcı yavaştır veya türün alternatif örneği çok fazla bellek alır.
Dan Violet Sagmiller

64

DefaultIfEmpty ve ardından First komutunu kullanabilirsiniz :

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

Ben fikrini seviyorum DefaultIfEmpty- bir varsayılan belirtilmesi gerekir TÜM API'leri ile çalışır: First(), Last()vb Bir kullanıcı olarak, yok varsayılan belirtmek için izin hangi API'leri hatırlamak gerekmez. Çok zarif!
KFL

Bu yuva yanıtıdır.
Jesse Williams

19

FirstOrDefault belgelerinden

Kaynak boşsa, [Varsayılan] [Döndürür];

Varsayılan belgelerden (T) :

referans türleri için null, sayısal değer türleri için sıfır döndürecek olan varsayılan anahtar kelime. Yapılar için, değer veya başvuru türleri olup olmadıklarına bağlı olarak başlatılan yapının her bir üyesini sıfır veya null değerine döndürür. Null olabilecek değer türleri için varsayılan, herhangi bir yapı gibi başlatılan bir System.Nullable döndürür.

Bu nedenle, türün bir başvuru veya değer türü olmasına bağlı olarak varsayılan değer null veya 0 olabilir, ancak varsayılan davranışı denetleyemezsiniz.


6

@Sloth tarafından yapılan yorumdan kopyalandı

Bunun yerine, örneğin YourCollection.FirstOrDefault()kullanabilirsiniz YourCollection.DefaultIfEmpty(YourDefault).First().

Misal:

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
Not DefaultIfEmptykoleksiyon boş IF döndürür varsayılan (0 öğe vardır). FirstÖrneğinizde olduğu gibi bir eşleşen ifade İLE kullanırsanız ve bu koşul herhangi bir öğe bulamazsa, dönüş değeriniz boş olur.
OriolBG

5

Bunu da yapabilirsiniz

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

Bu sadece linq - yipee kullanır!


2
Bu cevap ile C vitamini cevabı arasındaki tek fark FirstOrDefaultbunun yerine kullanmasıdır First. Msdn.microsoft.com/en-us/library/bb340482.aspx 'e göre önerilen kullanımFirst
Daniel

5

Aslında, NullReferenceExceptionkoleksiyonlarla çalışırken kaçınmak için iki yaklaşım kullanıyorum :

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 veya üstü için:

Üye erişimi gerçekleştirmeden önce null olup olmadığını sınamak için ?.veya öğesini kullanın Null koşullu Operatörler belgeleri?[

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

C # eski sürüm:

DefaultIfEmpty()Sekans boşsa varsayılan bir değer almak için kullanın . MSDN Belgeleri

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


1
İfade ağacı lambalarında boş yayılma operatörüne izin verilmez.
Lars335

1

Bunun yerine, örneğin YourCollection.FirstOrDefault()kullanabilirsiniz YourCollection.DefaultIfEmpty(YourDefault).First().


Yararlı olduğunu düşündüğünüzde, oylayabilirsiniz. Bu bir cevap değil.
jannagy02

1

Ben sadece benzer bir durum vardı ve her ihtiyaç duyduğumda arayan tarafında ilgilenmeden alternatif bir varsayılan değer döndürmek için izin veren bir çözüm arıyordu. Linq'in istediğimizi desteklememesi durumunda genellikle yaptığımız şey, onunla ilgilenen yeni bir uzantı yazmaktır. Ben de öyle yaptım. İşte geldim (test edilmemiş olsa da):

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

Bir süre geçtiğini biliyorum ama en popüler cevaba dayanarak buna ekleyeceğim, ancak küçük bir uzantıyla aşağıdakileri paylaşmak istiyorum:

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

Bu benim kendi örnek ile sorunları yaşadım gibi inline olarak adlandırmak için izin verir:

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

Bu yüzden benim için sadece bir varsayılan çözümleyici inline kullanılmasını istedim, her zamanki kontrolümü yapabilirim ve daha sonra bir işlevi geçebilirim, böylece bir sınıf kullanılmamış olsa bile somutlaştırılmaz, bunun yerine gerektiğinde yürütülecek bir işlev!


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.