?? Combinar por uma string vazia?

165

Algo que eu me vejo fazendo cada vez mais é verificar se uma string está vazia (como em ""ou nula) e um operador condicional.

Um exemplo atual:

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

Este é apenas um método de extensão, é equivalente a:

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

Como está vazio e não nulo, ??não fará o truque. Uma string.IsNullOrEmpty()versão do ??seria a solução perfeita. Estou pensando que deve haver uma maneira mais limpa de fazer isso (espero!), Mas não consegui encontrá-lo.

Alguém sabe de uma maneira melhor de fazer isso, mesmo que seja apenas no .Net 4.0?

Nick Craver
fonte
11
Apenas para atormentá-lo um pouco, é possível definir facilmente operadores binários ad-hoc personalizados (e unários, por sinal) em F #. Aqui let (|?) x y = if String.IsNullOrEmpty(x) then y else xe use-o como s.SiteNumber |? "No Number".
Stephen Swensen

Respostas:

72

Não há uma maneira integrada de fazer isso. Você pode fazer com que seu método de extensão retorne uma string ou um valor nulo, o que permitiria ao operador coalescente funcionar. Isso seria estranho, no entanto, e eu pessoalmente prefiro sua abordagem atual.

Como você já está usando um método de extensão, por que não fazer um que retorne o valor ou o padrão:

string result = s.SiteNumber.ConvertNullOrEmptyTo("No Number");
Reed Copsey
fonte
7
Acho que você está certo, e esta é a solução mais limpa atualmente disponível e que ainda pode ser lida. Eu adoraria algo como um ???operador em C # 5, quem sabe.
Nick Craver
2
e qual seria o ??? operador fazer? aceita valores padrão além de nulos? soa extremamente complicado na melhor das hipóteses #
bevacqua
1
Com expressões lambda, talvez? Por exemplo: suponha que "item" seja anulável, então ... item ?? x=> x.ToString() : null;
Isaac Llopis
@IsaacLlopis que acaba olhando confuso-er que a do OP trecho originais
Maksymiuk
133

C # já nos permite substituir valores por nullwith ??. Então, tudo o que precisamos é de uma extensão que converta uma string vazia em null, e então a usamos assim:

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

Classe de extensão:

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

Sei que essa é uma pergunta antiga - mas eu estava procurando uma resposta e nenhuma das opções acima se encaixava em minha necessidade e no que acabei 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");

EDIT: Uma maneira ainda mais compacta de escrever esta função seria:

static string Coalesce(params string[] strings) => strings.FirstOrDefault(s => !string.IsNullOrEmpty(s));
sfsr
fonte
1
Eu gosto, mas tive que corrigir um erro do compilador e torná-lo um pouco mais compacto.
22714 gregmac
Por que você chama isso Coalesce quando não reúne os valores, mas apenas seleciona aquele que não está vazio? É um nome confuso, cara.
precisa saber é o seguinte
8
Como Coalesce é o termo usado por muitos bancos de dados para fazer a mesma operação (encontre o primeiro valor não nulo). Unir cadeias é concatenação.
Cameron Forward
Melhor resposta se você éusing System.Linq
JoePC
É um trabalho elegante e agradável.
Mariano Luis Villa
16

Eu tenho algumas extensões de utilitário que eu gosto de 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;
}

Edit: Inspirado pela resposta do sfsr , adicionarei esta variante à minha caixa de ferramentas a partir de agora:

public static string Coalesce(this string str, params string[] strings)
{
    return (new[] {str})
        .Concat(strings)
        .FirstOrDefault(s => !string.IsNullOrEmpty(s));
}
Justin Morgan
fonte
1
Definitivamente, estou usando o termo "Coalesce", pois ele se parece mais com a intenção do operador coalescente nulo (??), embora eu tenha alterado para "CoalesceTo".
Llaughlin
O que o @prefixo no @defaultparâmetro faz? Eu nunca vi isso antes.
Drew Chapin
3
@druciferre - Isso apenas permite que você use defaultcomo um nome de variável, mesmo que seja uma palavra-chave reservada em C #.
Justin Morgan
1
@ Jimmyt1988 - Porque se aproxima da função T-SQL COALESCE padrão .
Justin Morgan
1
@ Jimmyt1988 - Também porque seleciona especificamente a primeira função não vazia em uma lista de tamanho arbitrário. É um detalhe sutil, mas a função T-SQL funciona da mesma maneira. O nome o torna intuitivo para quem conhece essa função, com ou sem documentação.
Justin Morgan.
6

Um método de extensão um pouco mais rápido do que o proposto anteriormente, talvez:

public static string Fallback(this string @this, string @default = "")
{
    return (@this == null || @this.Trim().Length == 0) ? @default : @this;
}
Chilidog
fonte
11
Por que não usar IsNullOrWhitespace em vez de apará-lo e comprê- lo?
weston
1
codesequência estática pública Coalesce (esta sequência @this, string @default = "") {return (@this == null || String.IsNullOrWhiteSpace (@this))? @ padrão: @; }
paul-2011
6

Uma das vantagens do operador de coalescência nula é o curto-circuito. Quando a primeira parte não é nula, a segunda parte não é avaliada. Isso pode ser útil quando o fallback requer uma operação cara.

Acabei com:

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
fonte
6

Eu simplesmente uso um método de extensão NullIfEmpty que sempre retornará nulo se a string estiver vazia permitindo ?? (Operador de coalizão nulo) a ser usado normalmente.

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

Isso então permite ?? para ser usado normalmente e facilita a leitura do encadeamento.

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

que tal um método de extensão de string ValueOrDefault ()

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

ou retorne nulo se a sequência estiver vazia:

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

Não tentei essas soluções embora.

devio
fonte
2
Eu gosto da opção 1 lá, embora eu a chame de algo mais semântico como Or (), para que eu possa escrever "string s = s.SiteNumber.Or (" Default ");"
Jvenema
2
Chamar algo ...OrDefault()seria confuso se não se comportasse como o resto dos ...OrDefault()métodos da estrutura . Goste ou não, a MS atribuiu um significado específico àquele nome e desvio desse comportamento nos métodos personalizados é desnecessariamente confuso para os usuários da sua API.
911911
3

Estou usando meu próprio método de extensão Coalesce de string. Como os que estão aqui estão usando LINQ e perdendo recursos absolutamente para operações demoradas (estou usando em loops apertados), compartilharei o meu:

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

Eu acho que é bastante simples, e você nem precisa se preocupar com valores de string nulos. Use-o assim:

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

Eu uso isto muito. Uma daquelas funções "utilitárias" das quais você não pode viver depois de usá-lo pela primeira vez :-)

Loudenvier
fonte
Acho que você não entende por que eles estão usando o LINQ e, como os parâmetros são avaliados antes das chamadas, você s2.Coalesce(s3)é executado mesmo quando não é necessário. Melhor usar uma NullIfEmpty()extensão e ??.
NetMage 16/04
@NetMage Eu posso garantir que as versões do LINQ têm um desempenho muito menor do que a que apresentei. Você pode criar uma referência simples para testar isso, se desejar. Sugiro usar github.com/dotnet/BenchmarkDotNet para evitar armadilhas comuns ao escrever código de benchmarking.
Loudenvier
0

Eu gosto da brevidade do seguinte método de extensão QQQpara isso, embora, é claro, um operador como? seria melhor. Mas podemos resolver isso permitindo que não apenas dois, mas três valores de opção de cadeia sejam comparados, o que encontra a necessidade de lidar de vez em quando (veja a segunda função abaixo).

#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
fonte
Se você gosta de nomes curtos, pode chamá-lo Or, e eu usaria a paramspalavra - chave, como as outras respostas, o que evita definições duplicadas para vários parâmetros.
Chiel ten Brinke 11/07/19
Obrigado pela ideia. Há muito que substituí esse nome por "FirstNotNull" em meu próprio uso. Em params, seria melhor não fazer isso para o cenário padrão ou dois, pois paramsfaz com que uma matriz seja alocada desnecessariamente quando você tiver apenas uma ou duas entradas padrão. Depois disso, faz sentido.
Nicholas Petersen