?? Coalesce per stringa vuota?


165

Qualcosa che mi ritrovo a fare sempre di più è il controllo di una stringa per vuoto (come in ""o null) e un operatore condizionale.

Un esempio attuale:

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

Questo è solo un metodo di estensione, è equivalente a:

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

Dal momento che è vuoto e non nullo, ??non farà il trucco. Una string.IsNullOrEmpty()versione di ??sarebbe la soluzione perfetta. Sto pensando che ci debba essere un modo più pulito di farlo (spero!), Ma non sono riuscito a trovarlo.

Qualcuno conosce un modo migliore per farlo, anche se è solo in .Net 4.0?


11
Solo per stuzzicarti un po ', puoi facilmente definire operatori binari personalizzati e ad hoc (e unari, per quella materia) in F #. Qui let (|?) x y = if String.IsNullOrEmpty(x) then y else xe usalo come s.SiteNumber |? "No Number".
Stephen Swensen,

Risposte:


72

Non esiste un modo integrato per farlo. Tuttavia, è possibile che il metodo di estensione restituisca una stringa o null, che consentirebbe all'operatore di coalescenza di funzionare. Questo sarebbe strano, tuttavia, e personalmente preferisco il tuo approccio attuale.

Poiché stai già utilizzando un metodo di estensione, perché non crearne uno che restituisca il valore o un valore predefinito:

string result = s.SiteNumber.ConvertNullOrEmptyTo("No Number");

7
Penso che tu abbia ragione, e questa è la soluzione più pulita attualmente disponibile che è ancora leggibile. Mi piacerebbe qualcosa come un ???operatore in C # 5, chi lo sa.
Nick Craver

2
e cosa sarebbe il ??? l'operatore fa? accetta valori predefiniti oltre ai null? nella migliore delle
ipotesi

1
Con le espressioni lambda forse? Ad esempio: supponiamo che "item" sia nullable, quindi ... item ?? x=> x.ToString() : null;
Isaac Llopis

@IsaacLlopis che finisce per sembrare disordinato rispetto allo snippet originale di OP
maksymiuk,

133

C # ci consente già di sostituire i valori nullcon ??. Quindi tutto ciò di cui abbiamo bisogno è un'estensione che converte una stringa vuota in null, e quindi la usiamo in questo modo:

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

Classe di estensione:

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

44

So che questa è una vecchia domanda, ma stavo cercando una risposta e nessuna delle precedenti è adatta alle mie esigenze e a ciò che ho finito per usare:

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

Uso:

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

EDIT: un modo ancora più compatto di scrivere questa funzione sarebbe:

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

1
Mi piace, ma ho dovuto correggere un errore del compilatore e renderlo un po 'più compatto.
Gregregac,

Perché chiami Coalesce quando non riunisce i valori, ma seleziona semplicemente quello che non è vuoto? È un nome confuso amico.
Jimmyt1988,

8
Perché Coalesce è il termine usato da molti database per fare la stessa operazione (trova il primo valore non nullo). Unire le stringhe è una concatenazione.
Cameron Forward

La migliore risposta se seiusing System.Linq
JoePC

È un lavoro elegante e piacevole.
Mariano Luis Villa,

16

Ho un paio di estensioni di utilità che mi piace usare:

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

Modifica: Ispirato dalla risposta di sfsr , aggiungerò questa variante alla mia cassetta degli attrezzi da ora in poi:

public static string Coalesce(this string str, params string[] strings)
{
    return (new[] {str})
        .Concat(strings)
        .FirstOrDefault(s => !string.IsNullOrEmpty(s));
}

1
Sto sicuramente usando il termine "Coalesce", in quanto ricorda più da vicino l'intento dell'operatore null coalescing (??), anche se l'ho cambiato in "CoalesceTo".
llaughlin,

Cosa fa il @prefisso sul @defaultparametro? Non l'ho mai visto prima.
Ha disegnato Chapin il

3
@druciferre - Ciò ti consente di usare solo defaultcome nome variabile anche se è una parola chiave riservata in C #.
Justin Morgan,

1
@ Jimmyt1988 - Perché si avvicina alla funzione COALESCE T-SQL standard .
Justin Morgan,

1
@ Jimmyt1988 - Anche perché seleziona specificamente la prima funzione non vuota in un elenco di lunghezza arbitraria. È un dettaglio sottile, ma la funzione T-SQL funziona allo stesso modo. Il nome lo rende intuitivo per chiunque conosca quella funzione, con o senza documentazione.
Justin Morgan,

6

Un metodo di estensione leggermente più veloce di quello proposto forse in precedenza:

public static string Fallback(this string @this, string @default = "")
{
    return (@this == null || @this.Trim().Length == 0) ? @default : @this;
}

11
Perché non usare IsNullOrWhitespace piuttosto che tagliarlo e allungarlo.
Weston,

1
codestringa statica pubblica Coalesce (questa stringa @this, stringa @default = "") {return (@this == null || String.IsNullOrWhiteSpace (@this))? @default: @this; }
paul-2011,

6

Uno dei vantaggi dell'operatore a coalescenza nulla è il cortocircuito. Quando la prima parte non è nulla, la seconda parte non viene valutata. Ciò può essere utile quando il fallback richiede un'operazione costosa.

Ho finito con:

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

Uso:

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

6

Uso semplicemente un metodo di estensione NullIfEmpty che restituirà sempre null se la stringa è vuota permettendo ?? (Null Coalescing Operator) da utilizzare normalmente.

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

Questo quindi permette ?? essere usato normalmente e rende il concatenamento di facile lettura.

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

3

che ne dite di un metodo di estensione stringa ValueOrDefault ()

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

o restituisce null se la stringa è vuota:

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

Non ho provato queste soluzioni però.


2
Mi piace l'opzione n. 1 lì, anche se la chiamerei qualcosa di più semantico come Or (), quindi potrei scrivere "string s = s.SiteNumber.Or (" Default ");"
jvenema,

2
Chiamare qualcosa ...OrDefault()sarebbe confuso se non si comportasse come il resto dei ...OrDefault()metodi del framework . Piaccia o no, MS ha dato un significato specifico a quel nome e deviare da quel comportamento in metodi personalizzati è inutilmente confuso per gli utenti della tua API.
mattmc3,

3

Sto usando un mio metodo di estensione Coalesce per stringa. Dal momento che quelli qui stanno usando LINQ e sprecando assolutamente risorse per operazioni che richiedono molto tempo (lo sto usando in cicli ristretti), condividerò il mio:

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

Penso che sia abbastanza semplice e non devi nemmeno preoccuparti di valori di stringa nulli. Usalo in questo modo:

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

Lo uso molto. Una di quelle funzioni di "utilità" di cui non puoi vivere senza averlo usato per la prima volta :-)


Non credo che tu capisca perché stanno usando LINQ, e poiché i parametri vengono valutati prima delle chiamate, il tuo s2.Coalesce(s3)viene eseguito anche quando non è necessario. Meglio usare NullIfEmpty()un'estensione e ??.
NetMage

@NetMage Ti posso garantire che le versioni di LINQ sono molto meno performanti di quella che ho presentato. Se lo desideri, puoi creare un semplice benchmark per testarlo. Suggerisco di usare github.com/dotnet/BenchmarkDotNet per evitare insidie ​​comuni durante la scrittura di codice di benchmarking.
Loudenvier,

0

Mi piace la brevità del seguente metodo di estensione QQQper questo, anche se ovviamente un operatore piace? sarebbe meglio. Ma possiamo farcela 1 consentendo di confrontare non solo due ma tre valori di opzioni di stringa, che si incontrano la necessità di gestire di tanto in tanto (vedere la seconda funzione di seguito).

#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

Se ti piacciono i nomi brevi potresti chiamarla Or, e io userei la paramsparola chiave, come le altre risposte, che evita definizioni duplicate per più parametri.
Chiel ten Brinke,

Grazie per l'idea Da tempo ho sostituito questo nome con "FirstNotNull" nel mio uso. Sì params, sarebbe meglio non farlo per uno o due scenari predefiniti, poiché paramsun array viene allocato inutilmente quando si hanno solo uno o due input predefiniti. Dopo ciò ha senso.
Nicholas Petersen,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.