it-swarm.com.de

FirstOrDefault: Standardwert ungleich null

Soweit ich es verstehe, kann die Methode FirstOrDefault() in Linq einen Default-Wert von etwas anderem als null zurückgeben. Was ich nicht herausgefunden habe, ist, welche anderen Dinge als null von dieser (und ähnlichen) Methode zurückgegeben werden können, wenn das Abfrageergebnis keine Elemente enthält. Gibt es eine bestimmte Möglichkeit, dass dies so eingerichtet werden kann, dass ein vordefinierter Wert als Standardwert zurückgegeben wird, wenn für eine bestimmte Abfrage kein Wert vorhanden ist?

113
Sachin Kainth

Allgemeiner Fall, nicht nur für Werttypen:

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

Wieder kann dies nicht wirklich sagen, ob war etwas in Ihrer Sequenz oder ob der erste Wert der Standardwert war.

Wenn Sie sich darum kümmern, könnten Sie so etwas tun

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

und verwenden als

var result = query.FirstOr(otherDefaultValue);

obwohl Mr. Steak darauf hinweist, könnte .DefaultIfEmpty(...).First() dies genauso gut tun.

39
Rawling

Wie ich es verstehe, kann die Methode FirstOrDefault () in Linq einen Standardwert von etwas anderem als null zurückgeben.

Nein, vielmehr gibt always den Standardwert für den Elementtyp ... zurück, der entweder eine Nullreferenz ist, der Nullwert eines nullfähigen Wertetyps oder der natürliche Wert "alle Nullen" für einen Nicht- nullwertbarer Werttyp.

Gibt es eine bestimmte Möglichkeit, dass dies so eingerichtet werden kann, dass ein vordefinierter Wert als Standardwert zurückgegeben wird, wenn für eine bestimmte Abfrage kein Wert vorhanden ist?

Für Referenztypen können Sie einfach Folgendes verwenden:

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

Natürlich wird auch Ihnen den "anderen Standardwert" geben, wenn der erste Wert vorhanden ist, aber eine Nullreferenz ist.

164
Jon Skeet

Sie können DefaultIfEmpty gefolgt von First verwenden:

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

Aus der Dokumentation für FirstOrDefault

[Returns] default (TSource), wenn die Quelle leer ist;

Aus der Dokumentation für default (T) :

das Standardschlüsselwort, das für Referenztypen Null und für numerische Werttypen Null zurückgibt. Bei Strukturen wird jedes initialisierte Element der Struktur auf null oder null zurückgegeben, abhängig davon, ob es sich um Wert- oder Referenztypen handelt. Bei nullwertfähigen Werttypen gibt default eine System.Nullable zurück, die wie jede Struktur initialisiert wird.

Daher kann der Standardwert null oder 0 sein, abhängig davon, ob der Typ ein Referenz- oder Werttyp ist, das Standardverhalten kann jedoch nicht gesteuert werden.

15
RB.

Übernommen von Kommentar von @sloth

Anstelle von YourCollection.FirstOrDefault() können Sie beispielsweise YourCollection.DefaultIfEmpty(YourDefault).First() verwenden.

Beispiel:

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

Sie können das auch tun 

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

Dies verwendet nur Linq-Yipee! 

4
BurnWithLife

Eigentlich verwende ich zwei Methoden, um NullReferenceException zu vermeiden, wenn ich mit Sammlungen arbeite:

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

Für C # 6.0 oder höher:

Verwenden Sie ?. oder ?[, um zu testen, ob der Wert null ist, bevor Sie einen Member-Zugriff durchführen. Dokumentation für null-bedingte Operatoren

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

C # ältere Version:

Verwenden Sie DefaultIfEmpty(), um einen Standardwert abzurufen, wenn die Sequenz leer ist . MSDN-Dokumentation

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

4
Samuel Diogo

Ich hatte gerade eine ähnliche Situation und suchte nach einer Lösung, die es mir ermöglicht, einen alternativen Standardwert zurückzugeben, ohne dass ich mich bei jedem Anrufer darum kümmern muss. Was wir normalerweise tun, wenn Linq nicht unterstützt, was wir wollen, ist das Schreiben einer neuen Erweiterung, die sich darum kümmert. Das ist, was ich tat. Folgendes habe ich mir ausgedacht (allerdings nicht getestet): 

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);
    }
}
1
harri

Anstelle von YourCollection.FirstOrDefault() könnten Sie beispielsweise YourCollection.DefaultIfEmpty(YourDefault).First() verwenden.

0
Raj.A

Ich weiß, dass es schon eine Weile her ist, aber ich füge hinzu, basierend auf der beliebtesten Antwort, aber mit einer kleinen Erweiterung, die ich gerne unten teilen möchte:

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

Dies ermöglicht es mir, es mit meinem eigenen Beispiel, mit dem ich Probleme hatte, als Inline zu bezeichnen:

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

Für mich wollte ich nur, dass ein Standard-Resolver inline verwendet wird. Ich kann meine üblichen Überprüfungen durchführen und dann eine Funktion übergeben, sodass eine Klasse nicht instanziiert wird, auch wenn sie nicht verwendet wird. Stattdessen muss diese Funktion ausgeführt werden.

0
Aaron Gibson