Como fazer ToString para um objeto possivelmente nulo?

97

Existe uma maneira simples de fazer o seguinte:

String s = myObj == null ? "" : myObj.ToString();

Sei que posso fazer o seguinte, mas realmente considero isso um hack:

String s = "" + myObj;

Seria ótimo se Convert.ToString () tivesse uma sobrecarga adequada para isso.

codymanix
fonte
3
Não vejo nada de errado com o primeiro. Se você considerar o segundo como um hack, sua melhor aposta é apenas escrever uma função de utilidade que execute a verificação de nulos.
Nick Larsen,
por favor, você pode ser mais preciso sobre sua pergunta
FosterZ
3
string.Format ("{0}", myObj) aceita valores nulos.
Holstebroe,
3
Com o C # 6.0, agora podemos usar operadores condicionais nulos, como theText? .ToString () ou theText? .Trim ()
Santosh
2
Por esta resposta Convert.ToString() faz exatamente a primeira coisa que você escreveu abaixo.
drzaus 01 de

Respostas:

175

Edição C # 6.0:

Com C # 6.0, agora podemos ter uma versão sucinta e livre de elenco do método original:

string s = myObj?.ToString() ?? "";

Ou até mesmo usando interpolação:

string s = $"{myObj}";

Resposta Original:

String s = (myObj ?? String.Empty).ToString();

ou

String s = (myObjc ?? "").ToString()

para ser ainda mais conciso.

Infelizmente, como foi apontado, você frequentemente precisará de um elenco em ambos os lados para fazer isso funcionar com tipos diferentes de String ou Object:

String s = (myObjc ?? (Object)"").ToString()
String s = ((Object)myObjc ?? "").ToString()

Portanto, embora pareça elegante, o elenco é quase sempre necessário e não é tão sucinto na prática.

Como sugerido em outro lugar, eu recomendo talvez usar um método de extensão para tornar isso mais limpo:

public static string ToStringNullSafe(this object value)
{
    return (value ?? string.Empty).ToString();
}
Andrew Hanlon
fonte
Isso vai compilar? O operador de coalescência não verifica os tipos?
Nick Larsen,
1
Funciona porque (object ?? string) retorna object, porque coalesce usa o tipo menos comum. Mas acho que isso não funcionará para interfaces porque não é possível decidir qual interface escolher (uma vez que várias interfaces são permitidas por classe).
codymanix de
1
Não é realmente a solução que eu esperava, mas vou aceitá-la
codymanix
4
Votar negativamente como aquele objeto é terrivelmente feio e envolve boxe.
steinar,
1
a primeira opção para c # 6 é muito elegante
Alexandre N.
40
string.Format("{0}", myObj);

string.Format irá formatar null como uma string vazia e chamar ToString () em objetos não nulos. Pelo que entendi, era isso que você procurava.

Holstebroe
fonte
3
Eu pessoalmente não gosto disso. O código pode ser insignificantemente menor do que String s = myObj == null? "": myObj.ToString (), mas você esconde totalmente o raciocínio por trás de seu método.
AGuyCalledGerald
Concedido, isso é microotimização, mas leva cerca de 5 vezes mais do que apenas "" + myObj. Mas eu li que cria strings extras. str.Concat(myObj)parece funcionar muito bem e é "ainda mais rápido".
drzaus 01 de
18

Seria ótimo se Convert.ToString () tivesse uma sobrecarga adequada para isso.

Houve um Convert.ToString(Object value)desde .Net 2.0 (aproximadamente 5 anos antes de este Q ser perguntado), que parece fazer exatamente o que você deseja:

http://msdn.microsoft.com/en-us/library/astxcyeh(v=vs.80).aspx

Estou perdendo / interpretando mal algo realmente óbvio aqui?

Rob Gilliam
fonte
1
Você não, eles fazem. Por que simples quando pode ser complicado :)
alex.peter
13

Com um método de extensão, você pode fazer isso:

public static class Extension
{
    public static string ToStringOrEmpty(this Object value)
    {
        return value == null ? "" : value.ToString();
    }
}

O seguinte não gravaria nada na tela e não geraria uma exceção:

        string value = null;

        Console.WriteLine(value.ToStringOrEmpty());
Pieter van Ginkel
fonte
As chamadas de método de extensão omitem a verificação usual de nulos ...?
Matti Virkkunen
2
Ele adiciona que você não precisa digitar "{0}" e pode escolher um nome de método que descreve o que está fazendo. Isso é especialmente útil quando você está fazendo isso muitas vezes. Você pode até nomear o método ToStringOrEmptyWhenNull.
Pieter van Ginkel
2
Se o OP pensa que isso string s = "" + myObj;é hackeado, chamar uma função em um objeto nulo deve cair no mesmo barco. Eu votaria contra isso, mas resolve o problema em questão, só discordo do uso. Objetos nulos devem ser lançados NullReferenceException, mesmo em métodos de extensão.
Nick Larsen
2
@NickLarsen: Eu concordo com você na maioria das ocasiões, mas às vezes você quer apenas a conveniência de uma chamada de método simples. O nome do método deve dar uma dica de que as referências nulas estão OK, como com a proposta para ToStringOrEmptyWhenNull.
Jordão
3
Os métodos de extensão não lançam quando o "primeiro" parâmetro (o thisparâmetro) porque eles são apenas açúcar sintático. Isso é x.SomeExtensionMethod()açúcar sintático para SomeStaticClass.SomeExtensionMethod(x);Assim, quando xé null, não estamos tentando invocar um método em um nullobjeto, mas sim passando um nullobjeto para um método estático. Se e somente se esse método verifica um nullparâmetro e lança ao encontrar tal um método de extensão "invocado" em um nulllançamento de objeto.
jason,
7

Eu discordo disso:

String s = myObj == null ? "" : myObj.ToString();

é um hack de qualquer forma. Acho que é um bom exemplo de código claro. É absolutamente óbvio o que você deseja alcançar e que espera null.

ATUALIZAR:

Vejo agora que você não estava dizendo que isso era um hack. Mas está implícito na pergunta que você acha que este não é o caminho a seguir. Na minha opinião, é definitivamente a solução mais clara.

Steinar
fonte
Ele não disse que esse método era hack. Na verdade, ele não disse o que havia de errado, exceto talvez insinuando que não era simples. Não acho que haja algo de errado com esse método. +1 porque eu faria dessa forma.
Mark Byers
Eu concordo com o OP ... já existe um operador de coalescência nulo em c # - veja minha postagem abaixo.
Andrew Hanlon,
Acho que o operador de coalescência nula é um pouco menos claro neste caso, no entanto, estaria igualmente bem em usá-lo.
steinar
@Pieter Muito justo. Mas é verdade. A questão é "Existe uma maneira simples de fazer o seguinte: ...". Essa maneira exata é simples!
steinar,
Eu nunca disse que esse método é um hack. Por favor, releia minha pergunta. Só sinto falta de uma pequena função no Framework que tem essa funcionalidade.
codymanix de
4
string s = String.Concat(myObj);

seria o caminho mais curto, eu acho, e também teria sobrecarga de desempenho insignificante. Porém, tenha em mente que não seria muito claro para o leitor do código qual é a intenção.

herzmeister
fonte
2
Esta é a forma explícita de "" + myObj, mas ainda pior em contar o que está acontecendo aqui. Interessante de qualquer maneira.
codymanix
@codymanix não acho que seja a mesma coisa - Concatna verdade, faz uma verificação de nulo por baixo e retorna string.Emptyou arg0.ToString(), que parece ter um desempenho um pouco melhor (quero dizer, estamos falando de ms aqui).
drzaus 01 de
2

na verdade eu não entendi o que você quer fazer. Pelo que entendi, você pode escrever este código de outra maneira como esta. Você está perguntando isso ou não? Você pode explicar mais?

string s = string.Empty;
    if(!string.IsNullOrEmpty(myObj))
    {
    s = myObj.ToString();
    }
Serkan Hekimoglu
fonte
Claro que posso escrever assim, mas quero uma chamada de função pequena e simples. Fiquei me perguntando por que não há nada assim no .NET BCL
codymanix
Você realmente precisa de uma função enquanto temos o método IsNullOrEmpty ()?
Serkan Hekimoglu
Por falar nisso, sempre quis que o operador de coalescência falhasse em seguida ao atingir uma referência nula em sua expressão fluente, mas entendo perfeitamente por que essa é uma ideia terrível. Uma substituição que permite que você faça isso também seria bom.
Nick Larsen,
1
string s = string.IsNullOrEmpty(myObj) ? string.Empty : myObj.ToString();
Nick Larsen,
2

Posso levar uma surra pela minha resposta, mas lá vai de qualquer maneira:

Eu simplesmente escreveria

string s = "" if (myObj! = null) {x = myObj.toString (); }

Há uma recompensa em termos de desempenho por usar o operador ternário? Eu não sei de cara.

E, claramente, como alguém mencionado acima, você pode colocar esse comportamento em um método como safeString (myObj) que permite a reutilização.

Jaydel
fonte
OMG, se o objeto for nulo, chame ToString () nele?
codymanix de
Vamos, isso é um erro de digitação. Substituí o primeiro = por! para torná-lo correto. Mas você realmente vai me -1 por erro de digitação?
Jaydel
2

Tive o mesmo problema e resolvi-o simplesmente lançando o objeto em uma corda. Isso também funciona para objetos nulos porque as strings podem ser nulas. A menos que você absolutamente não queira ter uma string nula, isso deve funcionar muito bem:

string myStr = (string)myObj; // string in a object disguise or a null
jahu
fonte
A solução mais curta e melhor. (Obj1 ?? "") .ToString () primeiro converte Obj1 como string também :)
TPAKTOPA
2

Alguns testes de desempenho (de velocidade) resumindo as várias opções, não que isso realmente importe #microoptimization (usando uma extensão do linqpad )

Opções

void Main()
{
    object objValue = null;
    test(objValue);
    string strValue = null;
    test(strValue);
}

// Define other methods and classes here
void test(string value) {
    new Perf<string> {
        { "coallesce", n => (value ?? string.Empty).ToString() },
        { "nullcheck", n => value == null ? string.Empty : value.ToString() },
        { "str.Format", n => string.Format("{0}", value) },
        { "str.Concat", n => string.Concat(value) },
        { "string +", n => "" + value },
        { "Convert", n => Convert.ToString(value) },
    }.Vs();
}

void test(object value) {
    new Perf<string> {
        { "coallesce", n => (value ?? string.Empty).ToString() },
        { "nullcheck", n => value == null ? string.Empty : value.ToString() },
        { "str.Format", n => string.Format("{0}", value) },
        { "str.Concat", n => string.Concat(value) },
        { "string +", n => "" + value },
        { "Convert", n => Convert.ToString(value) },
    }.Vs();
}

Provavelmente importante ressaltar que Convert.ToString(...) manterá uma string nula.

Resultados

Objeto

  • nullcheck 1,00x 1221 tiques decorridos (0,1221 ms) [em 10 mil repetições, 1,221E-05 ms por]
  • coallesce 1,14x 1387 ticks decorridos (0,1387 ms) [em 10K repetições, 1,387E-05 ms por]
  • string + 1,16x 1415 tiques decorridos (0,1415 ms) [em 10K repetições, 1,415E-05 ms por]
  • str.Concat 1,16x 1420 ticks decorridos (0,142 ms) [em 10 mil repetições, 1,42E-05 ms por]
  • Converter 1,58 x 1931 tiques decorridos (0,1931 ms) [em 10 mil repetições, 1,931E-05 ms por]
  • str.Format 5.45x 6655 ticks decorridos (0.6655 ms) [em 10K repetições, 6.655E-05 ms por]

Corda

  • nullcheck 1,00x 1190 ticks decorridos (0,119 ms) [em 10 mil repetições, 1,19E-05 ms por]
  • Converter 1,01 x 1200 tiquetaques decorridos (0,12 ms) [em 10 mil repetições, 1,2E-05 ms por]
  • string + 1,04x 1239 ticks decorridos (0,1239 ms) [em 10K repetições, 1,239E-05 ms por]
  • Coallesce 1,20x 1423 ticks decorridos (0,1423 ms) [em 10K repetições, 1,423E-05 ms por]
  • str.Concat 4,57x 5444 ticks decorridos (0,5444 ms) [em 10K repetições, 5,444E-05 ms por]
  • str.Format 5,67 x 6750 tiques decorridos (0,675 ms) [em 10K repetições, 6,75E-05 ms por]
drzaus
fonte
1

O comentário de Holstebroe seria sua melhor resposta:

string s = string.Format("{0}", myObj);

Se myObjfor nulo, Format coloca um valor de String vazia lá.

Ele também atende aos requisitos de uma linha e é fácil de ler.

jp2code
fonte
sim, mas o código não é claro. Quantos de seus colegas desenvolvedores saberão o que você está tentando realizar?
AGuyCalledGerald
0

Embora esta seja uma pergunta antiga e o OP pedisse C #, gostaria de compartilhar uma solução VB.Net para aqueles que trabalham com VB.Net em vez de C #:

Dim myObj As Object = Nothing
Dim s As String = If(myObj, "").ToString()

myObj = 42
s = If(myObj, "").ToString()

Infelizmente, o VB.Net não permite o operador? Após uma variável, então myObj? .ToString não é válido (pelo menos não em .Net 4.5, que usei para testar a solução). Em vez disso, uso o If para retornar uma string vazia no caso de myObj ist Nothing. Portanto, a primeira Tostring-Call retorna uma string vazia, enquanto a segunda (onde myObj não é Nothing) retorna "42".

Sascha
fonte