¿Cómo hacer ToString para un objeto posiblemente nulo?

97

¿Existe una forma sencilla de hacer lo siguiente?

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

Sé que puedo hacer lo siguiente, pero realmente lo considero un truco:

String s = "" + myObj;

Sería genial si Convert.ToString () tuviera una sobrecarga adecuada para esto.

codymanix
fuente
3
No veo nada malo en el primero. Si considera que el segundo es un truco, lo mejor es escribir una función de utilidad que realice la verificación nula.
Nick Larsen
por favor, ¿puede ser más preciso acerca de su pregunta?
FosterZ
3
string.Format ("{0}", myObj) acepta valores nulos.
Holstebroe
3
Con C # 6.0 ahora podemos usar operadores condicionales nulos, como theText? .ToString () o theText? .Trim ()
Santosh
2
Según esta respuesta, Convert.ToString() hace exactamente lo primero que escribiste debajo.
drzaus

Respuestas:

175

C # 6.0 Editar:

Con C # 6.0 ahora podemos tener una versión sucinta y sin cast del método original:

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

O incluso usando interpolación:

string s = $"{myObj}";

Respuesta original:

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

o

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

para ser aún más conciso.

Desafortunadamente, como se ha señalado, a menudo necesitará un molde en ambos lados para que esto funcione con tipos que no sean String u Object:

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

Por lo tanto, si bien puede parecer elegante, el elenco casi siempre es necesario y no es tan sucinto en la práctica.

Como se sugirió en otra parte, recomiendo tal vez usar un método de extensión para hacer este limpiador:

public static string ToStringNullSafe(this object value)
{
    return (value ?? string.Empty).ToString();
}
Andrew Hanlon
fuente
¿Se compilará esto? ¿El operador coalescente no comprueba los tipos?
Nick Larsen
1
Funciona desde que (object ?? string) devuelve object, porque coalesce usa el tipo menos común. Pero creo que esto no funcionará para las interfaces porque no puede decidir qué interfaz elegir (ya que se permiten múltiples interfaces por clase).
codymanix
1
No es realmente la solución que esperaba, pero la aceptaré
codymanix
4
Votar en contra ya que el elenco de ese objeto es terriblemente feo e implica boxeo.
steinar
1
la primera opción a c # 6 es muy elegante
Alexandre N.
40
string.Format("{0}", myObj);

string.Format formateará nulo como una cadena vacía y llamará a ToString () en objetos no nulos. Según tengo entendido, esto es lo que estabas buscando.

Holstebroe
fuente
3
A mí personalmente no me gusta esto. El código puede ser insignificantemente más corto que String s = myObj == null? "": myObj.ToString (), pero oculta totalmente el razonamiento detrás de su método.
AGuyCalledGerald
Por supuesto, esto es microoptimización, pero lleva aproximadamente 5 veces más que solo "" + myObj. Pero he leído que crea cadenas adicionales. str.Concat(myObj)parece funcionar bien y es "incluso más rápido".
drzaus
18

Sería genial si Convert.ToString () tuviera una sobrecarga adecuada para esto.

Ha habido un Convert.ToString(Object value).Net 2.0 desde entonces (aproximadamente 5 años antes de que se preguntara esta Q), que parece hacer exactamente lo que quieres:

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

¿Me estoy perdiendo / malinterpretando algo realmente obvio aquí?

Rob Gilliam
fuente
1
No lo haces, ellos lo hacen. Por qué simple cuando puede ser complicado :)
alex.peter
13

Con un método de extensión, puede lograr esto:

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

Lo siguiente no escribiría nada en la pantalla y no generaría una excepción:

        string value = null;

        Console.WriteLine(value.ToStringOrEmpty());
Pieter van Ginkel
fuente
¿Las llamadas al método de extensión omiten la comprobación habitual de nulos ...?
Matti Virkkunen
2
Agrega que no tiene que escribir "{0}" y que puede elegir un nombre de método que describa lo que está haciendo. Esto es especialmente útil cuando lo haces muchas veces. Incluso podría nombrar el método ToStringOrEmptyWhenNull.
Pieter van Ginkel
2
Si el OP cree que string s = "" + myObj;es un truco, llamar a una función en un objeto nulo debería caer en ese mismo barco. Yo rechazaría esto, pero resuelve el problema en cuestión, simplemente no estoy de acuerdo con el uso. Los objetos nulos deben lanzar NullReferenceException, incluso en métodos de extensión.
Nick Larsen
2
@NickLarsen: Estoy de acuerdo contigo en la mayoría de las ocasiones, pero a veces solo quieres la conveniencia de una simple llamada a un método. El nombre del método debería darle una pista de que las referencias nulas están bien, como con la propuesta de ToStringOrEmptyWhenNull.
Jordão
3
Los métodos de extensión no lanzan cuando el "primer" parámetro (el thisparámetro) porque son simplemente azúcar sintáctico. Es decir x.SomeExtensionMethod()es el azúcar sintáctica para SomeStaticClass.SomeExtensionMethod(x);tanto, cuando xestá nullno estamos tratando de invocar un método de un nullobjeto, sino más bien pasar un nullobjeto a un método estático. Si y solo si ese método busca un nullparámetro y lo lanza cuando lo encuentra, un método de extensión se "invoca" en un nullobjeto.
jason
7

No estoy de acuerdo con esto:

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

es un truco de cualquier forma. Creo que es un buen ejemplo de código claro. Es absolutamente obvio lo que quiere lograr y que espera nulo.

ACTUALIZAR:

Ahora veo que no estabas diciendo que esto era un truco. Pero está implícito en la pregunta que piensas que de esta manera no es el camino a seguir. En mi opinión, definitivamente es la solución más clara.

steinar
fuente
No dijo que este método fuera un truco. De hecho, no dijo qué estaba mal, excepto quizás para dar a entender que no era simple. No creo que haya nada de malo en este método. +1 porque lo haría de esta manera.
Mark Byers
Estoy de acuerdo con OP ... ya hay un operador de fusión nulo en c #; vea mi publicación a continuación.
Andrew Hanlon
Creo que el operador de fusión nula es un poco menos claro en este caso, sin embargo, estaría igual de bien si lo usara.
steinar
@Pieter Bastante justo. Pero lo hace. La pregunta es "¿Existe una forma sencilla de hacer lo siguiente: ...". ¡Esa forma exacta es simple!
steinar
Nunca dije que este método es un truco. Por favor relea mi pregunta. Solo echo de menos una pequeña función en el Framework que tiene esta funcionalidad.
codymanix
4
string s = String.Concat(myObj);

Supongo que sería el camino más corto y también tendría una sobrecarga de rendimiento insignificante. Sin embargo, tenga en cuenta que no quedará muy claro para el lector del código cuál es la intención.

herzmeister
fuente
2
Esta es la forma explícita de "" + myObj, pero es aún peor al contar lo que está sucediendo aquí. Interesante de todos modos.
codymanix
@codymanix, no creo que sea lo mismo, en Concatrealidad hace una verificación nula debajo y devuelve string.Emptyo arg0.ToString(), lo que parece ser un poco más eficiente (quiero decir, estamos hablando de ms aquí).
drzaus
2

en realidad no entendí qué es lo que quieres hacer. Según tengo entendido, puede escribir este código de otra manera como esta. ¿Estás preguntando esto o no? ¿Puedes explicarme mas?

string s = string.Empty;
    if(!string.IsNullOrEmpty(myObj))
    {
    s = myObj.ToString();
    }
Serkan Hekimoglu
fuente
Claro que puedo escribirlo así, pero quiero una pequeña llamada de función simple, me preguntaba por qué no hay nada como esto en .NET BCL
codymanix
¿Realmente necesitas una función mientras tenemos el método IsNullOrEmpty ()?
Serkan Hekimoglu
En esa nota, siempre he querido que el operador de fusión falle la próxima vez que llegue a una referencia nula en su expresión fluida, pero entiendo completamente por qué es una idea terrible. Una anulación que le permita hacer eso también estaría bien.
Nick Larsen
1
string s = string.IsNullOrEmpty(myObj) ? string.Empty : myObj.ToString();
Nick Larsen
2

Puede que me golpeen por mi respuesta, pero aquí va de todos modos:

Simplemente escribiría

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

¿Hay alguna recompensa en términos de rendimiento por utilizar el operador ternario? No lo sé de la parte superior de mi cabeza.

Y claramente, como alguien mencionó anteriormente, puede poner este comportamiento en un método como safeString (myObj) que permite la reutilización.

Jaydel
fuente
Dios mío, si el objeto es nulo, ¿llamar a ToString () en él?
codymanix
Vamos, eso es un error tipográfico. He reemplazado el primer = con! para hacerlo correcto. ¿Pero realmente me vas a -1 por un error tipográfico?
Jaydel
2

Tuve el mismo problema y lo resolví simplemente lanzando el objeto a una cadena. Esto también funciona para objetos nulos porque las cadenas pueden ser nulos. A menos que no quiera tener una cadena nula, esto debería funcionar bien:

string myStr = (string)myObj; // string in a object disguise or a null
jahu
fuente
La mejor y más corta solución. (Obj1 ?? "") .ToString () en realidad primero lanza Obj1 como cadena también :)
TPAKTOPA
2

Algunas pruebas de rendimiento (de velocidad) que resumen las diversas opciones, no es que realmente importe #microoptimización (usando una extensión linqpad )

Opciones

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

Probablemente sea importante señalar que Convert.ToString(...)conservará una cadena nula.

Resultados

Objeto

  • nullcheck 1.00x 1221 tics transcurridos (0.1221 ms) [en 10K repeticiones, 1.221E-05 ms por]
  • carbonlesce 1,14x 1387 tics transcurridos (0,1387 ms) [en 10K repeticiones, 1,387E-05 ms por]
  • cadena + 1,16 x 1415 tics transcurridos (0,1415 ms) [en 10K repeticiones, 1,415E-05 ms por]
  • str.Concat 1,16 x 1420 tics transcurridos (0,142 ms) [en 10.000 repeticiones, 1,42E-05 ms por]
  • Convertir 1,58 x 1931 tics transcurridos (0,1931 ms) [en 10K repeticiones, 1,931E-05 ms por]
  • str.Format 5.45x 6655 tics transcurridos (0.6655 ms) [en 10K repeticiones, 6.655E-05 ms por]

Cuerda

  • nullcheck 1,00 x 1190 tics transcurridos (0,119 ms) [en 10K repeticiones, 1,19E-05 ms por]
  • Convertir 1.01x 1200 ticks transcurridos (0.12 ms) [en 10K repeticiones, 1.2E-05 ms por]
  • string + 1.04x 1239 tics transcurridos (0.1239 ms) [en 10K repeticiones, 1.239E-05 ms por]
  • carbonlesce 1,20 x 1423 tics transcurridos (0,1423 ms) [en 10.000 repeticiones, 1,423E-05 ms por]
  • str.Concat 4.57x 5444 tics transcurridos (0.5444 ms) [en 10K repeticiones, 5.444E-05 ms por]
  • str.Format 5,67 x 6750 tics transcurridos (0,675 ms) [en 10K repeticiones, 6,75E-05 ms por]
drzaus
fuente
1

El comentario de Holstebroe sería su mejor respuesta:

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

Si myObjes nulo, Format coloca un valor de Cadena vacía allí.

También satisface el requisito de una línea y es fácil de leer.

jp2code
fuente
sí, pero el código no está claro. ¿Cuántos de sus compañeros desarrolladores sabrán lo que está tratando de lograr?
AGuyCalledGerald
0

Aunque esta es una pregunta antigua y el OP pidió C #, me gustaría compartir una solución VB.Net para aquellos que trabajan con VB.Net en lugar de C #:

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

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

Desafortunadamente, VB.Net no permite el operador? Después de una variable, por lo que myObj? .ToString no es válido (al menos no en .Net 4.5, que utilicé para probar la solución). En su lugar, uso If para devolver una cadena vacía en caso de que myObj no sea Nothing. Entonces, el primer Tostring-Call devuelve una cadena vacía, mientras que el segundo (donde myObj no es Nothing) devuelve "42".

Sascha
fuente