?? 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?


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");

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

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

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

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

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

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;

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.


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 :-)


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

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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.