?? ¿Coalesce por una cadena vacía?

165

Algo que me encuentro haciendo más y más es verificar que una cadena esté vacía (como en ""o nula) y un operador condicional.

Un ejemplo actual:

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

Este es solo un método de extensión, es equivalente a:

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

Como está vacío y no es nulo, ??no funcionará. Una string.IsNullOrEmpty()versión de ??sería la solución perfecta. Estoy pensando que tiene que haber una forma más limpia de hacerlo (¡espero!), Pero no he podido encontrarlo.

¿Alguien sabe de una mejor manera de hacer esto, incluso si es solo en .Net 4.0?

Nick Craver
fuente
11
Solo para tentarlo un poco, puede definir fácilmente operadores binarios personalizados y ad-hoc (y unarios, para el caso) en F #. Aquí let (|?) x y = if String.IsNullOrEmpty(x) then y else xy úsalo como s.SiteNumber |? "No Number".
Stephen Swensen

Respuestas:

72

No hay una forma integrada de hacer esto. Sin embargo, puede hacer que su método de extensión devuelva una cadena o un valor nulo, lo que permitiría que el operador de fusión funcione. Sin embargo, esto sería extraño, y personalmente prefiero su enfoque actual.

Como ya está utilizando un método de extensión, ¿por qué no hacer uno que devuelva el valor o un valor predeterminado?

string result = s.SiteNumber.ConvertNullOrEmptyTo("No Number");
Reed Copsey
fuente
77
Creo que tienes razón, y esta es la solución más limpia disponible actualmente que todavía es legible. Sin ???embargo, me encantaría algo como un operador en C # 5, quién sabe.
Nick Craver
2
y cual seria el ??? operador hacer? tomar valores predeterminados además de nulos? sonidos extremadamente complicado en el mejor
bevacqua
1
¿Con expresiones lambda tal vez? Por ejemplo: suponga que "elemento" es anulable, entonces ... item ?? x=> x.ToString() : null;
Isaac Llopis
@IsaacLlopis que termina buscando desordenado-er que el fragmento original de la OP
Maksymiuk
133

C # ya nos permite sustituir valores por nullcon ??. Entonces, todo lo que necesitamos es una extensión que convierta una cadena vacía en null, y luego la usamos así:

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

Clase de extensión:

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
fuente
44

Sé que esta es una vieja pregunta, pero estaba buscando una respuesta y nada de lo anterior se ajustaba a mi necesidad, así como lo que terminé usando:

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

Uso:

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

EDITAR: Una forma aún más compacta de escribir esta función sería:

static string Coalesce(params string[] strings) => strings.FirstOrDefault(s => !string.IsNullOrEmpty(s));
sfsr
fuente
1
Me gusta, pero tuve que arreglar un error del compilador y lo hice un poco más compacto.
gregmac
¿Por qué llama a esto Coalesce cuando no reúne los valores, sino que simplemente selecciona el que no está vacío? Es un nombre confuso, amigo.
Jimmyt1988
8
Porque Coalesce es el término utilizado por muchas bases de datos para hacer la misma operación (encontrar el primer valor no nulo). Unir cadenas es concatenación.
Cameron Forward
La mejor respuesta si estásusing System.Linq
JoePC
Eso es elegante, buen trabajo.
Mariano Luis Villa
16

Tengo un par de extensiones de utilidad que me gusta usar:

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

Editar: Inspirado por la respuesta de sfsr, de ahora en adelante agregaré esta variante a mi caja de herramientas:

public static string Coalesce(this string str, params string[] strings)
{
    return (new[] {str})
        .Concat(strings)
        .FirstOrDefault(s => !string.IsNullOrEmpty(s));
}
Justin Morgan
fuente
1
Definitivamente estoy usando el término "Coalesce", ya que se parece más a la intención del operador de fusión nulo (??), aunque lo cambié a "CoalesceTo".
llaughlin
¿Qué hace el @prefijo en el @defaultparámetro? Nunca he visto eso antes.
Drew Chapin
3
@druciferre: eso solo le permite usarlo defaultcomo un nombre de variable aunque sea una palabra clave reservada en C #.
Justin Morgan
1
@ Jimmyt1988: porque se aproxima a la función estándar T-SQL COALESCE .
Justin Morgan
1
@ Jimmyt1988 - También porque selecciona específicamente la primera función no vacía en una lista de longitud arbitraria. Es un detalle sutil, pero la función T-SQL funciona de la misma manera. El nombre lo hace intuitivo para cualquiera que conozca esa función, con o sin documentación.
Justin Morgan
6

Un método de extensión un poco más rápido que el propuesto anteriormente quizás:

public static string Fallback(this string @this, string @default = "")
{
    return (@this == null || @this.Trim().Length == 0) ? @default : @this;
}
Chilidog
fuente
11
¿Por qué no utilizar IsNullOrWhitespace en lugar de recortarlo y alargarlo ?
Weston
1
codePublic static string Coalesce (esta cadena @this, string @default = "") {return (@this == null || String.IsNullOrWhiteSpace (@this))? @default: @this; }
paul-2011
6

Una de las ventajas del operador de fusión nula es que cortocircuita. Cuando la primera parte no es nula, la segunda parte no se evalúa. Esto puede ser útil cuando la recuperación requiere una operación costosa.

Terminé 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);
Wensveen
fuente
6

Simplemente uso un método de extensión NullIfEmpty que siempre devolverá nulo si la cadena está vacía permitiendo ?? (Operador de fusión nula) que se utilizará normalmente.

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

Entonces esto permite ?? se usa normalmente y hace que el encadenamiento sea fácil de leer.

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

¿qué tal un método de extensión de cadena ValueOrDefault ()

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

o devolver nulo si la cadena está vacía:

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

Sin embargo, no probé estas soluciones.

devio
fuente
2
Me gusta la opción n. ° 1, aunque lo llamaría algo más semántico como Or (), para poder escribir "string s = s.SiteNumber.Or (" Default ");"
jvenema
2
Llamar a algo ...OrDefault()sería confuso si no se comportara como el resto de los ...OrDefault()métodos del marco . Nos guste o no, MS ha dado un significado específico a que nombrar y desviarse de ese comportamiento en los métodos personalizados es innecesariamente confuso para los usuarios de su API.
mattmc3
3

Estoy usando un método de extensión Coalesce de cadena propio. Dado que los que están aquí están usando LINQ y están desperdiciando recursos para operaciones que requieren mucho tiempo (lo estoy usando en ciclos cerrados), compartiré el mío:

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

Creo que es bastante simple, y ni siquiera necesita molestarse con valores de cadena nulos. Úselo así:

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

Lo uso mucho. Una de esas funciones de "utilidad" sin las que no puedes vivir después de usarla por primera vez :-)

Loudenvier
fuente
No creo que entiendas por qué están usando LINQ, y dado que los parámetros se evalúan antes de las llamadas, tu s2.Coalesce(s3)se ejecuta incluso cuando no es necesario. Es mejor usar una NullIfEmpty()extensión y ??.
NetMage
@NetMage Puedo garantizarle que las versiones de LINQ son mucho menos eficaces que la que presenté. Puede crear un punto de referencia simple para probar esto si lo desea. Sugiero usar github.com/dotnet/BenchmarkDotNet para evitar errores comunes al escribir código de evaluación comparativa.
Loudenvier
0

Me gusta la brevedad del siguiente método de extensión QQQpara esto, aunque, por supuesto, ¿un operador como? seria mejor. Pero podemos aumentar esto al permitir que se comparen no solo dos, sino tres valores de opciones de cadena, que encuentra la necesidad de manejar de vez en cuando (vea la segunda función a continuación).

#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
fuente
Si le gustan los nombres cortos, podría llamarlo Or, y usaría la paramspalabra clave, como las otras respuestas, lo que evita definiciones duplicadas para múltiples parámetros.
Chiel ten Brinke
Gracias por la idea Hace mucho tiempo que reemplacé este nombre con "FirstNotNull" en mi propio uso. Encendido params, sería mejor no hacerlo para el escenario predeterminado o dos, ya que paramshace que una matriz se asigne innecesariamente cuando solo tiene una o dos entradas predeterminadas. Después de eso tiene sentido.
Nicholas Petersen