?? Koaleszenz für leere Schnur?

164

Etwas, das ich immer mehr mache, ist das Überprüfen einer Zeichenfolge auf Leerzeichen (wie in ""oder null) und einen bedingten Operator.

Ein aktuelles Beispiel:

s.SiteNumber.IsNullOrEmpty() ? "No Number" : s.SiteNumber;

Dies ist nur eine Erweiterungsmethode, die entspricht:

string.IsNullOrEmpty(s.SiteNumber) ? "No Number" : s.SiteNumber;

Da es leer und nicht null ist, reicht ??es nicht aus. Eine string.IsNullOrEmpty()Version von ??wäre die perfekte Lösung. Ich denke, es muss einen saubereren Weg geben, dies zu tun (ich hoffe!), Aber ich war ratlos, es zu finden.

Kennt jemand einen besseren Weg, dies zu tun, auch wenn es nur in .Net 4.0 ist?

Nick Craver
quelle
11
Um Sie ein wenig zu verführen, können Sie in F # auf einfache Weise benutzerdefinierte Ad-hoc-Binäroperatoren (und auch unäre Operatoren) definieren. Hier let (|?) x y = if String.IsNullOrEmpty(x) then y else xund benutze es gerne s.SiteNumber |? "No Number".
Stephen Swensen

Antworten:

72

Es gibt keine eingebaute Möglichkeit, dies zu tun. Sie können Ihre Erweiterungsmethode jedoch dazu bringen, eine Zeichenfolge oder null zurückzugeben, damit der Koaleszenzoperator arbeiten kann. Dies wäre jedoch seltsam, und ich persönlich bevorzuge Ihren derzeitigen Ansatz.

Da Sie bereits eine Erweiterungsmethode verwenden, können Sie eine erstellen, die den Wert oder einen Standardwert zurückgibt:

string result = s.SiteNumber.ConvertNullOrEmptyTo("No Number");
Reed Copsey
quelle
7
Ich denke, Sie haben Recht, und dies ist die sauberste derzeit verfügbare Lösung, die noch lesbar ist. Ich würde so etwas wie einen ???Operator in C # 5 lieben , wer weiß.
Nick Craver
2
und was würde das ??? Betreiber tun? Standardwerte zusätzlich zu Nullen nehmen? klingt bestenfalls extrem kompliziert
bevacqua
1
Mit Lambda-Ausdrücken vielleicht? Zum Beispiel: Nehmen wir an, "item" ist nullbar, dann ... item ?? x=> x.ToString() : null;
Isaac Llopis
@IsaacLlopis, die am Ende unordentlicher aussieht als OPs ursprüngliches Snippet
maksymiuk
132

C # schon können wir für Werte ersetzen nullmit ??. Wir brauchen also nur eine Erweiterung, die eine leere Zeichenfolge in konvertiert null, und dann verwenden wir sie wie folgt :

s.SiteNumber.NullIfEmpty() ?? "No Number";

Erweiterungsklasse:

public static class StringExtensions
{
    public static string NullIfEmpty(this string s)
    {
        return string.IsNullOrEmpty(s) ? null : s;
    }
    public static string NullIfWhiteSpace(this string s)
    {
        return string.IsNullOrWhiteSpace(s) ? null : s;
    }
}
RedFilter
quelle
44

Ich weiß, dass dies eine alte Frage ist - aber ich suchte nach einer Antwort und keine der oben genannten Fragen entsprach meinen Bedürfnissen und dem, was ich letztendlich verwendet habe:

private static string Coalesce(params string[] strings)
{
    return strings.FirstOrDefault(s => !string.IsNullOrEmpty(s));
}

Verwendung:

string result = Coalesce(s.SiteNumber, s.AltSiteNumber, "No Number");

EDIT: Eine noch kompaktere Art, diese Funktion zu schreiben, wäre:

static string Coalesce(params string[] strings) => strings.FirstOrDefault(s => !string.IsNullOrEmpty(s));
sfsr
quelle
1
Ich mag, musste aber einen Compilerfehler beheben und machte es etwas kompakter.
Gregmac
Warum nennt man das Coalesce, wenn es die Werte nicht zusammenbringt, sondern nur den auswählt, der nicht leer ist? Es ist ein verwirrender Name, Alter.
Jimmyt1988
8
Weil Coalesce der Begriff ist, der von vielen Datenbanken verwendet wird, um dieselbe Operation auszuführen (ersten Wert ungleich Null suchen). Das Zusammenfügen von Zeichenfolgen ist eine Verkettung.
Cameron Forward
Beste Antwort, wenn Sie sindusing System.Linq
JoePC
Das ist elegant, gute Arbeit.
Mariano Luis Villa
16

Ich habe einige Dienstprogrammerweiterungen, die ich gerne verwende:

public static string OrDefault(this string str, string @default = default(string))
{
    return string.IsNullOrEmpty(str) ? @default : str;
}

public static object OrDefault(this string str, object @default)
{
    return string.IsNullOrEmpty(str) ? @default : str;
}

Bearbeiten: Inspiriert von der Antwort von sfsr werde ich diese Variante von nun an zu meiner Toolbox hinzufügen:

public static string Coalesce(this string str, params string[] strings)
{
    return (new[] {str})
        .Concat(strings)
        .FirstOrDefault(s => !string.IsNullOrEmpty(s));
}
Justin Morgan
quelle
1
Ich verwende definitiv den Begriff "Coalesce", da er der Absicht des Null-Coalescing-Operators (??) ähnlicher ist, obwohl ich ihn in "CoalesceTo" geändert habe.
Laughlin
Was macht das @Präfix des @defaultParameters? Das habe ich noch nie gesehen.
Drew Chapin
3
@druciferre - Damit können Sie nur defaulteinen Variablennamen verwenden, obwohl es sich um ein reserviertes Schlüsselwort in C # handelt.
Justin Morgan
1
@ Jimmyt1988 - Weil es sich der Standardfunktion von T-SQL COALESCE annähert .
Justin Morgan
1
@ Jimmyt1988 - Auch weil es speziell die erste nicht leere Funktion in einer Liste beliebiger Länge auswählt . Es ist ein subtiles Detail, aber die T-SQL-Funktion funktioniert genauso. Der Name macht es für jeden intuitiv, der diese Funktion mit oder ohne Dokumentation kennt.
Justin Morgan
6

Eine etwas schnellere Verlängerungsmethode als früher vorgeschlagen:

public static string Fallback(this string @this, string @default = "")
{
    return (@this == null || @this.Trim().Length == 0) ? @default : @this;
}
Chilidog
quelle
11
Warum nicht IsNullOrWhitespace verwenden, anstatt es zu kürzen und zu verlängern ?
Weston
1
codeöffentliche statische Zeichenfolge Coalesce (diese Zeichenfolge @this, Zeichenfolge @default = "") {return (@this == null || String.IsNullOrWhiteSpace (@this))? @default: @this; }
Paul-2011
6

Einer der Vorteile des Null-Koaleszenz-Operators besteht darin, dass er kurzschließt. Wenn der erste Teil nicht null ist, wird der zweite Teil nicht ausgewertet. Dies kann nützlich sein, wenn der Fallback eine teure Operation erfordert.

Am Ende hatte ich:

public static string Coalesce(this string s, Func<string> func)
{
    return String.IsNullOrEmpty(s) ? func() : s;
}

Verwendung:

string navigationTitle = model?.NavigationTitle.
    Coalesce(() => RemoteTitleLookup(model?.ID)). // Expensive!
    Coalesce(() => model?.DisplayName);
wensveen
quelle
6

Ich verwende einfach eine NullIfEmpty-Erweiterungsmethode, die immer null zurückgibt, wenn die Zeichenfolge leer ist. (Null Coalescing Operator) wird wie gewohnt verwendet.

public static string NullIfEmpty(this string s)
{
    return s.IsNullOrEmpty() ? null : s;
}

Dies ermöglicht dann? Wird wie gewohnt verwendet und ist leicht zu lesen.

string string1 = string2.NullIfEmpty() ?? string3.NullIfEmpty() ?? string4;
jjr2000
quelle
3

Wie wäre es mit einer String-Erweiterungsmethode ValueOrDefault ()

public static string ValueOrDefault(this string s, string sDefault)
{
    if (string.IsNullOrEmpty(s))
        return sDefault;
    return s;
}

oder geben Sie null zurück, wenn die Zeichenfolge leer ist:

public static string Value(this string s)
{
    if (string.IsNullOrEmpty(s))
        return null;
    return s;
}

Ich habe diese Lösungen jedoch nicht ausprobiert.

devio
quelle
2
Ich mag dort Option 1, obwohl ich es etwas semantischeres wie Or () nennen würde, damit ich "string s = s.SiteNumber.Or (" Default ");"
Jvenema
2
Das Aufrufen von etwas ...OrDefault()wäre verwirrend, wenn es sich nicht wie die übrigen ...OrDefault()Methoden des Frameworks verhalten würde . Ob es Ihnen gefällt oder nicht, MS hat dieser Benennung eine bestimmte Bedeutung gegeben, und die Abweichung von diesem Verhalten bei benutzerdefinierten Methoden ist für Benutzer Ihrer API unnötig verwirrend.
Mattmc3
3

Ich verwende eine eigene String-Coalesce-Erweiterungsmethode. Da die hier LINQ verwenden und Ressourcen für zeitintensive Operationen verschwenden (ich verwende es in engen Schleifen), werde ich meine teilen:

public static class StringCoalesceExtension
{
    public static string Coalesce(this string s1, string s2)
    {
        return string.IsNullOrWhiteSpace(s1) ? s2 : s1;
    }
}

Ich denke, es ist ganz einfach, und Sie müssen sich nicht einmal mit Null-String-Werten beschäftigen. Verwenden Sie es so:

string s1 = null;
string s2 = "";
string s3 = "loudenvier";
string s = s1.Coalesce(s2.Coalesce(s3));
Assert.AreEqual("loudenvier", s);

Ich benutze es oft. Eine dieser "Utility" -Funktionen, auf die Sie nach der ersten Verwendung nicht mehr verzichten können :-)

Loudenvier
quelle
Ich glaube nicht, dass Sie verstehen, warum sie LINQ verwenden, und da Parameter vor Aufrufen ausgewertet werden, wird Ihr s2.Coalesce(s3)Programm auch dann ausgeführt, wenn es nicht benötigt wird. Besser eine NullIfEmpty()Erweiterung verwenden und ??.
NetMage
@NetMage Ich kann Ihnen garantieren, dass die LINQ-Versionen viel weniger leistungsfähig sind als die von mir vorgestellte. Sie können einen einfachen Benchmark erstellen, um dies zu testen, wenn Sie dies wünschen. Ich schlage vor, github.com/dotnet/BenchmarkDotNet zu verwenden, um häufige Fallstricke beim Schreiben von Benchmarking-Code zu vermeiden.
Loudenvier
0

Ich mag die Kürze der folgenden Erweiterungsmethode QQQdafür, obwohl natürlich ein Operator wie? wäre besser. Wir können dies jedoch verbessern, indem wir nicht nur zwei, sondern drei Zeichenfolgenoptionswerte vergleichen lassen, auf die hin und wieder verzichtet werden muss (siehe zweite Funktion unten).

#region QQ

[DebuggerStepThrough]
public static string QQQ(this string str, string value2)
{
    return (str != null && str.Length > 0)
        ? str
        : value2;
}

[DebuggerStepThrough]
public static string QQQ(this string str, string value2, string value3)
{
    return (str != null && str.Length > 0)
        ? str
        : (value2 != null && value2.Length > 0)
            ? value2
            : value3;
}


// Following is only two QQ, just checks null, but allows more than 1 string unlike ?? can do:

[DebuggerStepThrough]
public static string QQ(this string str, string value2, string value3)
{
    return (str != null)
        ? str
        : (value2 != null)
            ? value2
            : value3;
}

#endregion
Nicholas Petersen
quelle
Wenn Sie Kurznamen mögen, können Sie es nennen Or, und ich würde das paramsSchlüsselwort wie die anderen Antworten verwenden, wodurch doppelte Definitionen für mehrere Parameter vermieden werden.
Chiel ten Brinke
Danke für die Idee. Ich habe diesen Namen in meiner eigenen Verwendung längst durch "FirstNotNull" ersetzt. Ein params, es ist besser, dies nicht für das Standard-Szenario oder zwei zu tun, da paramsein Array unnötig zugewiesen wird, wenn Sie nur die Standardeinträge ein oder zwei haben. Danach macht es Sinn.
Nicholas Petersen