?? Coalesce pour une chaîne vide?

165

Quelque chose que je fais de plus en plus est de vérifier une chaîne pour vide (comme in ""ou null) et un opérateur conditionnel.

Un exemple actuel:

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

Ceci est juste une méthode d'extension, c'est équivalent à:

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

Comme il est vide et non nul, ??cela ne fera pas l'affaire. Une string.IsNullOrEmpty()version de ??serait la solution parfaite. Je pense qu'il doit y avoir un moyen plus propre de faire cela (j'espère!), Mais j'ai été incapable de le trouver.

Quelqu'un connaît-il une meilleure façon de faire cela, même si ce n'est que dans .Net 4.0?

Nick Craver
la source
11
Pour vous séduire un peu, vous pouvez facilement définir des opérateurs binaires (et unaires, d'ailleurs) personnalisés et ad hoc en F #. Ici let (|?) x y = if String.IsNullOrEmpty(x) then y else xet utilisez-le comme s.SiteNumber |? "No Number".
Stephen Swensen

Réponses:

72

Il n'y a pas de moyen intégré de le faire. Vous pouvez cependant faire en sorte que votre méthode d'extension renvoie une chaîne ou une valeur nulle, ce qui permettrait à l'opérateur de fusion de fonctionner. Ce serait étrange, cependant, et je préfère personnellement votre approche actuelle.

Puisque vous utilisez déjà une méthode d'extension, pourquoi ne pas en créer une qui renvoie la valeur ou une valeur par défaut:

string result = s.SiteNumber.ConvertNullOrEmptyTo("No Number");
Reed Copsey
la source
7
Je pense que vous avez raison, et c'est la solution la plus propre actuellement disponible qui soit encore lisible. J'adorerais quelque chose comme un ???opérateur en C # 5, qui sait.
Nick Craver
2
et que serait le ??? opérateur faire? prendre des valeurs par défaut en plus des valeurs nulles? semble au mieux extrêmement compliqué
bevacqua
1
Avec des expressions lambda peut-être? Par exemple: supposons que "item" soit nul, alors ... item ?? x=> x.ToString() : null;
Isaac Llopis
@IsaacLlopis qui finit par avoir l'air désordonné que l'extrait de
code
133

C # nous permet déjà de remplacer des valeurs par nullavec ??. Donc, tout ce dont nous avons besoin est une extension qui convertit une chaîne vide en null, puis nous l'utilisons comme ceci:

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

Classe d'extension:

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
la source
44

Je sais que c'est une vieille question - mais je cherchais une réponse et aucune des réponses ci-dessus ne correspondait à mes besoins ainsi qu'à ce que j'ai fini par utiliser:

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

Usage:

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

EDIT: Une manière encore plus compacte d'écrire cette fonction serait:

static string Coalesce(params string[] strings) => strings.FirstOrDefault(s => !string.IsNullOrEmpty(s));
sfsr
la source
1
J'aime, mais j'ai dû corriger une erreur du compilateur et le rendre un peu plus compact.
gregmac du
Pourquoi appelez-vous cela Coalesce quand il ne rassemble pas les valeurs, mais sélectionne simplement celle qui n'est pas vide? C'est un mec de nom déroutant.
Jimmyt1988
8
Parce que Coalesce est le terme utilisé par de nombreuses bases de données pour effectuer la même opération (trouver la première valeur non nulle). Joindre des chaînes ensemble est une concaténation.
Cameron Forward
Meilleure réponse si vous êtesusing System.Linq
JoePC
C'est un travail élégant et agréable.
Mariano Luis Villa
16

J'ai quelques extensions utilitaires que j'aime utiliser:

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

Edit: Inspiré par la réponse de sfsr , je vais désormais ajouter cette variante à ma boîte à outils:

public static string Coalesce(this string str, params string[] strings)
{
    return (new[] {str})
        .Concat(strings)
        .FirstOrDefault(s => !string.IsNullOrEmpty(s));
}
Justin Morgan
la source
1
J'utilise définitivement le terme "Coalesce", car il ressemble plus étroitement à l'intention de l'opérateur de fusion nul (??), bien que je l'ai changé en "CoalesceTo".
llaughlin
Que fait le @préfixe du @defaultparamètre? Je n'ai jamais vu ça auparavant.
Drew Chapin
3
@druciferre - Cela vous permet simplement d'utiliser defaultcomme nom de variable même s'il s'agit d'un mot clé réservé en C #.
Justin Morgan
1
@ Jimmyt1988 - Parce qu'il se rapproche de la fonction standard T-SQL COALESCE .
Justin Morgan
1
@ Jimmyt1988 - Aussi parce qu'il sélectionne spécifiquement la première fonction non vide dans une liste de longueur arbitraire. C'est un détail subtil, mais la fonction T-SQL fonctionne de la même manière. Le nom le rend intuitif pour quiconque connaît cette fonction, avec ou sans documentation.
Justin Morgan
6

Une méthode d'extension légèrement plus rapide que celle proposée précédemment peut-être:

public static string Fallback(this string @this, string @default = "")
{
    return (@this == null || @this.Trim().Length == 0) ? @default : @this;
}
Chilidog
la source
11
Pourquoi ne pas utiliser IsNullOrWhitespace plutôt que de le couper et de le prolonger .
weston
1
codechaîne statique publique Coalesce (cette chaîne @this, string @default = "") {return (@this == null || String.IsNullOrWhiteSpace (@this))? @ par défaut: @this; }
paul-2011
6

L'un des avantages de l'opérateur à fusion nulle est qu'il court-circuite. Lorsque la première partie n'est pas nulle, la deuxième partie n'est pas évaluée. Cela peut être utile lorsque la solution de secours nécessite une opération coûteuse.

J'ai fini avec:

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

Usage:

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

J'utilise simplement une méthode d'extension NullIfEmpty qui retournera toujours null si la chaîne est vide permettant ?? (Null Coalescing Operator) à utiliser normalement.

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

Cela permet alors ?? à utiliser normalement et facilite la lecture du chaînage.

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

que diriez-vous d'une méthode d'extension de chaîne ValueOrDefault ()

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

ou retourne null si la chaîne est vide:

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

Je n'ai pas essayé ces solutions cependant.

devio
la source
2
J'aime l'option n ° 1, même si je l'appellerais quelque chose de plus sémantique comme Or (), donc je pourrais écrire "string s = s.SiteNumber.Or (" Default ");"
jvenema
2
Appeler quelque chose ...OrDefault()serait déroutant s'il ne se comportait pas comme le reste des ...OrDefault()méthodes du framework . Qu'on le veuille ou non, MS a donné une signification spécifique à cette dénomination et s'écarter de ce comportement dans les méthodes personnalisées est inutilement déroutant pour les utilisateurs de votre API.
mattmc3
3

J'utilise ma propre méthode d'extension de chaîne Coalesce. Puisque ceux-ci utilisent LINQ et gaspillent absolument des ressources pour des opérations gourmandes en temps (je l'utilise en boucles serrées), je vais partager le mien:

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

Je pense que c'est assez simple et que vous n'avez même pas besoin de vous soucier des valeurs de chaîne nulles. Utilisez-le comme ceci:

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

Je l'utilise beaucoup. Une de ces fonctions "utilitaires" dont vous ne pouvez pas vous passer après l'avoir utilisée pour la première fois :-)

Loudenvier
la source
Je ne pense pas que vous compreniez pourquoi ils utilisent LINQ, et comme les paramètres sont évalués avant les appels, votre s2.Coalesce(s3)est exécuté même lorsque cela n'est pas nécessaire. Mieux vaut utiliser une NullIfEmpty()extension et ??.
NetMage
@NetMage Je peux vous garantir que les versions LINQ sont beaucoup moins performantes que celle que j'ai présentée. Vous pouvez créer un benchmark simple pour tester cela si vous le souhaitez. Je suggère d'utiliser github.com/dotnet/BenchmarkDotNet pour éviter les pièges courants lors de l'écriture de code d'analyse comparative.
Loudenvier le
0

J'aime la brièveté de la méthode d'extension suivante QQQpour cela, bien que bien sûr un opérateur comme? serait mieux. Mais nous pouvons augmenter cela en permettant non seulement de comparer deux mais trois valeurs d'options de chaîne, ce que l'on rencontre le besoin de gérer de temps en temps (voir la deuxième fonction ci-dessous).

#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
la source
Si vous aimez les noms courts, vous pouvez l'appeler Or, et j'utiliserais le paramsmot - clé, comme les autres réponses, ce qui évite les définitions en double pour plusieurs paramètres.
Chiel ten Brinke
Merci pour l'idée. J'ai depuis longtemps remplacé ce nom par "FirstNotNull" dans mon propre usage. Sur params, il serait préférable de ne pas faire cela pour le scénario par défaut ou deux, car cela paramsprovoque l'allocation d'un tableau inutilement lorsque vous n'avez qu'une ou deux entrées par défaut. Après cela, cela a du sens.
Nicholas Petersen