¿Por qué / cuándo sería apropiado anular ToString?

103

Estoy estudiando C # y me pregunto cuál ToStringpodría ser el punto y el beneficio de anular , como se muestra en el siguiente ejemplo.

¿Podría hacerse esto de una manera más simple, usando un método común sin la anulación?

public string GetToStringItemsHeadings
{
    get { return string.Format("{0,-20} {1, -20}", "Office Email", "Private Email"); }
}


public override string ToString()
{
    string strOut = string.Format("{0,-20} {1, -20}", m_work, m_personal);
    return strOut;
}
3D-kreativ
fuente
4
Depende si realmente desea usarlo para mostrar el objeto en la interfaz de usuario en cualquier lugar, de lo contrario, generalmente es solo para depurar; verá la salida de ToString en la ventana de observación de VS. (Pero también puede lograr eso con atributos en la clase). Dado que tiene encabezados y resultados, parece que este programa lo está usando para volcar el objeto usando Console.WriteLine(obj.GetToStringItemsHeadings); Console.WriteLine(obj);o similar.
Rup
2
Realmente no entiendo la pregunta. ¿Se puede hacer de otra manera? Si. Pero, ¿por qué quieres hacerlo de otra manera?
Konrad Rudolph
5
Realmente omitiría GetToStringel nombre de la propiedad ... Entonces obtendría, por ejemploConsole.WriteLine(obj.ItemHeadings)
Svish
y cambie la propiedad ItemHeadings para que sea estática, ya que no necesita ningún "esto".
eFloh
Es posible que desee consultar esta pregunta: stackoverflow.com/questions/7906828/oo-design-advice-tostring
Yuri Ghensev

Respuestas:

127
  • ¿Necesitas anular ToString? No.

  • ¿Puede obtener una representación de cadena de su objeto de otra manera? Si.

Pero al usar, ToStringestás usando un método que es común a todos los objetos y, por lo tanto, otras clases conocen este método. Por ejemplo, siempre que el marco .NET desee convertir un objeto en una representación de cadena, ToStringes un candidato principal (hay otros, si desea proporcionar opciones de formato más elaboradas).

Concretamente,

Console.WriteLine(yourObject);

invocaría yourObject.ToString().

Konrad Rudolph
fuente
13
Si. También esto es muy útil en el desarrollo de MVC o ASP.Net donde @yourObjecty <%=yourObject%>van a ser "ToString-ed".
Ben Lesh
4
Además, al completar un cuadro combinado, ToString es la llamada predeterminada para obtener el texto del elemento
Davi Fiamenghi
3
Tenga cuidado con esa práctica. Historia real: un estudiante anuló ToString con un método que además de devolver una cadena, cambió los datos. Estaba depurando el programa, pero mientras el programa se encontraba en un punto de interrupción, el valor seguía cambiando. Aparentemente, cada vez que verificaba el valor ToString se ejecutaba, el valor cambiaba aunque el programa no se estuviera ejecutando.
JNF
3
@JNF ¿Qué es “tal práctica”? ToStringen realidad, no debería cambiar el estado de un objeto. Pero esto no es lo que defendí.
Konrad Rudolph
8
@JNF No acepto eso. ToStringestá destinado a ser anulado. Período. Hacer una advertencia sobre esta afirmación no es productivo. Si lo hace mal, es culpa suya. De hecho, su estudiante violó el punto 4 de la lista oficial de pautas para anular ToStringque se publicó en la respuesta de David Anderson.
Konrad Rudolph
129

Solo le daré la respuesta directamente de las Pautas de diseño de marco de la Serie de desarrollo de .NET.

EVITE lanzar excepciones deToString

CONSIDERE devolver una cadena única asociada con la instancia.

CONSIDERE que la salida ToStringsea ​​una entrada válida para cualquier método de análisis de este tipo.

ASEGÚRESE de que ToStringno tenga efectos secundarios observables.

DEBE reportar información sensible a la seguridad a través de una anulación de ToStringsolo después de exigir un permiso apropiado. Si la solicitud de permiso falla, devuelva una cadena que excluya la información sensible a la seguridad.

El Object.ToStringmétodo está destinado a ser utilizado con fines generales de visualización y depuración. La implementación predeterminada simplemente proporciona el nombre del tipo de objeto. La implementación predeterminada no es muy útil y se recomienda que se anule el método.

anule ToStringsiempre que se pueda devolver una cadena interesante legible por humanos. La implementación predeterminada no es muy útil y una implementación personalizada casi siempre puede proporcionar más valor.

DO prefieren un nombre más de un ID único legible, pero no.

También vale la pena mencionarlo como Chris Sells también explica en las pautas que a ToStringmenudo es peligroso para las interfaces de usuario. En general, mi regla general es exponer una propiedad que se usaría para vincular información a la interfaz de usuario y dejar la ToStringanulación para mostrar información de diagnóstico al desarrollador. También puede decorar su tipo DebuggerDisplayAttributetambién.

DO tratar de mantener la cadena de regresar de ToStringcorto. El depurador utiliza ToStringpara obtener una representación textual de un objeto que se mostrará al desarrollador. Si la cadena es más larga de lo que puede mostrar el depurador, la experiencia de depuración se ve obstaculizada.

HAGA formato de cadena según la cultura del hilo actual al devolver información dependiente de la cultura.

DEBE proporcionar sobrecarga ToString(string format), o implementar IFormattable, si la cadena devuelta ToStringes sensible a la cultura o si hay varias formas de formatear la cadena. Por ejemplo, DateTimeproporciona la sobrecarga y los implementos IFormattable.

NO devuelva una cadena vacía o nula deToString

Juro por estas pautas, y debería hacerlo. No puedo decirles cómo ha mejorado mi código solo con esta guía para ToString. Lo mismo ocurre con cosas como IEquatable(Of T)y IComparable(Of T). Estas cosas hacen que su código sea muy funcional, y no se arrepentirá de tomarse el tiempo adicional para implementarlo.

Personalmente, nunca he usado ToString mucho para las interfaces de usuario, siempre he expuesto una propiedad o método de algún tipo. La mayor parte del tiempo se debe utilizar ToStringpara fines de depuración y desarrollo. Úselo para mostrar información de diagnóstico importante.

David Anderson
fuente
6
Buena respuesta, ¿son estas las pautas a las que se refiere msdn.microsoft.com/en-us/library/ms229042.aspx ?
Jodrell
2
@Jodrell Creo que está tomado de las Pautas de diseño
Jim Schubert
6
Se necesita una mejor cita.
Ben Voigt
13
No sabía que SO se estaba convirtiendo en Wikipedia.
David Anderson
14
No lo es, pero cuando cita pautas, proporcionar la fuente es una ventaja innegable.
Falanwe
39

La anulación le ToString()permite proporcionar una representación de cadena útil y legible por humanos de una clase.

Esto significa que la salida puede revelar información útil sobre su clase. Por ejemplo, si tuvieras una clase Person, podrías optar por tener como ToString()salida la identificación de la persona, su nombre, su apellido, etc. Esto es extremadamente útil al depurar o registrar.

Con respecto a su ejemplo, es difícil saber si su anulación es útil sin saber qué es esta clase, pero la implementación en sí está bien.

Rob Levine
fuente
13

Siempre es apropiado, pero considere cuidadosamente las intenciones detrás de lo que está mostrando.

Una mejor pregunta sería preguntar:

¿Por qué se anularía ToString ()?

ToString () es la ventana al estado de un objeto. Énfasis en el estado como requisito. Los lenguajes fuertemente orientados a objetos como Java / C # abusan del modelo de programación orientada a objetos encapsulando todo en una clase. Imagina que estás codificando en un lenguaje que no sigue el modelo fuerte de OOP; considere si usaría una clase o una función. Si lo usa como una función (es decir, verbo, acción) y el estado interno solo se mantiene temporalmente entre la entrada / salida, ToString () no agregará valor.

Como otros han mencionado, es importante considerar lo que genera con ToString () porque podría ser utilizado por el depurador u otros sistemas.

Me gusta imaginar el método ToString como el parámetro --help de un objeto. Debe ser breve, legible, obvio y fácil de mostrar. Debe mostrar lo que el objeto no es lo que hace . Con todo eso en mente, consideremos ...

Caso de uso: análisis de un paquete TCP:

No es una captura de red solo a nivel de aplicación, sino algo con más carne como una captura pcap.

Desea sobrecargar ToString () solo para la capa TCP para poder imprimir datos en la consola. ¿Qué incluiría? Podría volverse loco y analizar todos los detalles de TCP (es decir, TCP es complejo) ...

Que incluye:

  • Puerto de origen
  • Puerto de destino
  • Secuencia de números
  • Número de acuse de recibo
  • Desplazamiento de datos
  • Banderas
  • Desplazamiento de ventana
  • Suma de comprobación
  • Puntero urgente
  • Opciones (ni siquiera voy a ir)

Pero, ¿le gustaría recibir toda esa basura si estuviera llamando a TCP.ToString () en 100 paquetes? Por supuesto que no, sería una sobrecarga de información. La elección fácil y obvia es también la más sensata ...

Exponga lo que la gente esperaría ver:

  • Puerto de origen
  • Puerto de destino

Prefiero una salida sensible que sea fácil de analizar para los humanos, pero YMMV .

TCP:[destination:000, source:000]

Nada complejo, la salida no es para que las máquinas la analicen (es decir, a menos que las personas estén abusando de su código), el propósito previsto es la legibilidad humana.

Pero, ¿qué pasa con el resto de esa jugosa información de la que hablé antes? ¿No es eso útil también? Llegaré a eso, pero primero ...


ToString () uno de los métodos más valiosos y subutilizados de todos los tiempos

Por dos razones:

  1. La gente no entiende para qué sirve ToString ()
  2. A la clase base 'Objeto' le falta otro método de cadena igualmente importante.

Razón 1: no abuse de la utilidad de ToString ():

Mucha gente usa ToString () para extraer una representación de cadena simple de un objeto. El manual de C # incluso dice:

ToString es el método de formateo principal en .NET Framework. Convierte un objeto en su representación de cadena para que sea adecuado para su visualización.

Monitor, sin procesamiento adicional. Eso no significa que tome mi bonita representación de cadena del paquete TCP anterior y extraiga el puerto de origen usando una expresión regular :: cringe ::.

El derecho forma de hacer las cosas es llamar a ToString () directamente en la propiedad SourcePort (que por cierto es un ushort, por lo que ToString () ya debería estar disponible).

Si necesita algo más robusto para empaquetar el estado de un objeto complejo para el análisis automático, será mejor que utilice una estrategia de serialización estructurada.

Afortunadamente, estas estrategias son muy comunes:

  • ISerializable (C #)
  • Pepinillo (Python)
  • JSON (Javascript o cualquier lenguaje que lo implemente)
  • JABÓN
  • etc ...

Nota: A menos que esté usando PHP porque, herp-derp, hay una función para eso :: snicker ::

Razón 2: ToString () no es suficiente:

Todavía tengo que ver un lenguaje que implemente esto en el núcleo, pero he visto y usado variaciones de este enfoque en la naturaleza.

Algunos de los cuales incluyen:

  • ToVerboseString ()
  • ToString (detallado = verdadero)

Básicamente, ese desorden peludo del estado de un paquete TCP debería describirse para la legibilidad humana. Para evitar 'vencer a un caballo muerto' hablando de TCP, 'señalaré con el dedo' el caso # 1 donde creo que ToString () y ToVerboseString () están infrautilizados ...

Caso de uso: matrices:

Si usa principalmente un idioma, probablemente se sienta cómodo con el enfoque de ese idioma. Para personas como yo, que pasan de un idioma a otro, la variedad de enfoques puede resultar irritante.

Es decir, la cantidad de veces que esto me ha irritado es mayor que la suma de todos los dedos de cada dios hindú combinados.

Hay varios casos en los idiomas de uso común hacks y un pocos que consiguen que la derecha . Algunos requieren la reinvención de la rueda, algunos hacen un volcado poco profundo, otros hacen un volcado profundo, ninguno de ellos funciona como me gustaría ...

Lo que estoy pidiendo es un enfoque muy simple:

print(array.ToString());

Salidas: 'Array [x]' o 'Array [x] [y]'

Donde x es el número de elementos en la primera dimensión e y es el número de elementos en la segunda dimensión o algún valor que indique que la segunda dimensión es irregular (¿rango mínimo / máximo tal vez?).

Y:

print(array.ToVerboseString());

Imprime todo el she-bang en letras bonitas porque aprecio las cosas bonitas.

Con suerte, esto arroja algo de luz sobre un tema que me ha molestado durante mucho tiempo. Por lo menos, esparcí un pequeño cebo para que los PHPers votaran negativamente esta respuesta.

Evan Solla
fuente
Escribiste: "Todavía tengo que ver un lenguaje que implemente esto en el núcleo". Me parece que Python se acerca bastante a esto; por ejemplo, la impresión de una matriz proporciona todos sus datos de forma concisa. Eso es una cosa que extraño en C # / Java, aunque me gusta su rigor. Y cuando necesite ir un poco más allá de ToString (), sería bueno tener algo como la sintaxis de comprensión de listas de Python; Supongo que string.Join () es similar, aunque menos flexible.
Jon Coombs
1
@JCoombs No estoy en desacuerdo, las comprensiones son geniales. Atravesar estructuras de datos en Python a través de comprensiones hace la vida mucho más fácil, pero ese enfoque aún requiere un conocimiento profundo de la estructura interna del objeto. Algo que no siempre es obvio con las clases que se han importado de una biblioteca externa. Es una buena práctica que los autores de la biblioteca anulen ToString () y proporcionen una representación legible por humanos útil, pero también sería bueno tener un método de volcado general que genere la estructura del objeto en un formato legible por humanos estructurado general (ex JSON).
Evan Plaice
Excelente punto. Entonces, en lugar de mostrar nada más que básicamente un typeof, podría serializarse automáticamente a algo como JSON. (Hasta ahora, solo he visto objetos .NET serializados en XML, pero soy nuevo en .NET) ¿Pero entonces el peligro podría ser la tentación de tratar ToString () como legible por máquina? (Por ejemplo, espere poder deserializarlo.)
Jon Coombs
1
@JCoombs Sí, a nivel funcional, JSON y XML tienen el mismo propósito. Mucha gente usa ToString como una salida legible por máquina; si eso es malo o no es discutible. Mi mayor problema es, a menudo, que es complicado mostrar el estado de un objeto en un formato legible por humanos. La implementación de ToString () a menudo se subutiliza y leer el estado de un objeto fuera de las listas de vigilancia del depurador es una molestia. Las representaciones serializadas son buenas como respaldo para mostrar el estado completo de un objeto, pero las mejores implementaciones de ToString son la mejor opción.
Evan Plaice
Si ToString()está destinado a la depuración, ¿por qué es la única forma de extraer todo el texto de un StringBuilder, una función importante?
Dan W
9

Se trata tanto de buenas prácticas como de cualquier otra cosa, en realidad.

ToString()se utiliza en muchos lugares para devolver una representación de cadena de un objeto, generalmente para el consumo de un ser humano. A menudo, esa misma cuerda se puede usar para rehidratar el objeto (piense enint o DateTimepor ejemplo), pero eso no siempre es un hecho (un árbol, por ejemplo, puede tener una representación de cadena útil que simplemente muestra un recuento, pero claramente no puede usar eso para reconstruirlo).

En particular, el depurador usará esto para mostrar la variable en las ventanas de observación, ventanas inmediatas, etc. ToString es realmente invaluable para la depuración.

En general, también, este tipo a menudo tendrá un miembro explícito que también devuelve una cadena. Por ejemplo, un par Lat / Long podría tener ToDecimalDegreesque devuelve, "-10, 50"por ejemplo, pero también podría tener ToDegreesMinutesSeconds, ya que ese es otro formato para un par Lat / Long. Ese mismo tipo también podría anularse ToStringcon uno de esos para proporcionar un 'predeterminado' para cosas como depuración, o incluso potencialmente para cosas como renderizar páginas web (por ejemplo, la @construcción en Razor escribe el ToString()resultado de una expresión que no es de cadena en el flujo de salida).

Andras Zoltan
fuente
6

object.ToString()convierte un objeto en su representación de cadena. Si desea cambiar lo que se devuelve cuando un usuario llama ToString()a una clase que ha creado, deberá anular ToString()esa clase.

Robbie
fuente
6

Si bien creo que ya se ha proporcionado la información más útil, agregaré mis dos centavos:

  • ToString()está destinado a ser anulado. Su implementación predeterminada devuelve el nombre del tipo que, aunque puede ser útil a veces (especialmente cuando se trabaja con muchos objetos), no es suficiente en la gran mayoría de las veces.

  • Recuerde que, a efectos de depuración, puede confiar en DebuggerDisplayAttribute. Puedes leer más sobre esto aquí .

  • Como regla general, en las POCO siempre puede anular ToString(). Los POCO son una representación estructurada de datos, que generalmente pueden convertirse en una cadena.

  • Diseñe ToString para que sea una representación textual de su objeto. Tal vez sus campos y datos principales, tal vez una descripción de cuántos elementos hay en la colección, etc.

  • Siempre trate de encajar esa cadena en una sola línea y tenga solo la información esencial. Si tiene una Personclase con propiedades de nombre, dirección, número, etc., devuelva solo los datos principales (nombre algún número de identificación).

  • Tenga cuidado de no anular una buena implementación de ToString(). Algunas clases de framework ya se implementan ToString(). Anular esa implementación predeterminada es algo malo: la gente esperará un resultado determinado ToString()y obtendrá otro.

No tengas miedo de consumir ToString(). Lo único con lo que tendría cuidado es devolver información confidencial. Aparte de eso, el riesgo es mínimo. Claro, como algunos han señalado, otras clases usarán su ToString siempre que busquen información. Pero diablos, ¿ cuándo se considerará mejor devolver el nombre del tipo que obtener información real?

Bruno Brant
fuente
5

Si no anula ToString, obtendrá la implementación de sus clases base, que Objectes solo el nombre de tipo corto de la clase.

Si desea alguna otra implementación más significativa o útil de ToString, anótela.


Esto puede resultar útil cuando se utiliza una lista de su tipo como fuente de datos para a, ListBoxya ToStringque se mostrará automáticamente.

Otra situación ocurre cuando desea pasar su tipo al String.Formatque invoca ToStringpara obtener una representación de su tipo.

Jodrell
fuente
4

Algo que nadie más ha mencionado todavía: al anular ToString(), también puede considerar implementar IFormattablepara poder hacer lo siguiente:

 public override ToString() {
   return string.Format("{0,-20} {1, -20}", m_work, m_personal);
 }

 public ToString(string formatter) {
   string formattedEmail = this.ToString();
   switch (formatter.ToLower()) {
     case "w":
       formattedEmail = m_Work;
       break;
     case "p":
       formattedEmail = m_Personal;
       break;
     case "hw":
       formattedEmail = string.Format("mailto:{0}", m_Work);
       break;
   }
   return formattedEmail;
}

Lo que puede resultar útil.

Zhaph - Ben Duguid
fuente
¿Puede dar un ejemplo de una clase de estructura que proporcione ese método para anular? Lo único relacionado que conozco en el marco es la IFormattibleinterfaz.
tm1
@ tm1 Cualquier objeto que herede System.Objecttendrá un ToString()método reemplazable , pero tiene razón en que el método del formateador no debería ser overridey realmente debería hacer referencia a la IFormattibleinterfaz para una conformidad completa.
Zhaph - Ben Duguid
Casi - IFormattabletoma un IFormatProviderparámetro. Gracias por editar tu publicación.
tm1
De hecho, tiene dos métodos, el valor que estaba expresando estaba en el que se muestra;)
Zhaph - Ben Duguid
3

Un beneficio de anular ToString () es el soporte de herramientas de Resharper: Alt + Ins -> "Formatear miembros" y escribe el ToString () por usted.

Daniel James Bryars
fuente
2

En algunos casos, facilita la lectura de valores de clases personalizadas en la ventana de observación del depurador. Si sé exactamente lo que quiero ver en la ventana de visualización, cuando anulo ToString con esa información, lo veo.

filólogo
fuente
1

Al definir estructuras (de hecho, el usuario primitivas) Me parece que es una buena práctica tener a juego ToString, y Parsey TryParsemétodos, en particular para la serialización XML. En este caso, convertirá todo el estado en una cadena, de modo que pueda leerse más tarde.

Sin embargo, las clases son estructuras más compuestas que normalmente serán demasiado complejas para usar ToStringy Parse. Sus ToStringmétodos, en lugar de guardar todo el estado, pueden ser una descripción simple que le ayude a identificar su estado, como un identificador único como un nombre o ID, o tal vez una cantidad para una lista.

Además, como dijo Robbie, la anulación le ToStringpermite llamar ToStringa una referencia tan básica como el tipo object.

Dave Cousineau
fuente
1

Puede usar eso cuando tenga un objeto con un significado no intuitivo de representación de cadena, como persona. Entonces, si necesita, por ejemplo, imprimir a esta persona, puede usar esta anulación para preparar su formato.

NickF
fuente
1

El Object.ToStringmétodo debe usarse solo con fines de depuración. La implementación predeterminada muestra el nombre del tipo de objeto que no es muy útil. Considere anular este método para proporcionar mejor información para el diagnóstico y la depuración. Tenga en cuenta que las infraestructuras de registro a menudo también utilizan el método ToString, por lo que encontrará estos fragmentos de texto en sus archivos de registro.

No devuelva recursos de texto localizados dentro del Object.ToStringmétodo. La razón es que el método ToString siempre debe devolver algo que el desarrollador pueda entender. Es posible que el desarrollador no hable todos los idiomas que admite la aplicación.

Implemente la IFormattableinterfaz cuando desee devolver un texto localizado fácil de usar. Esta interfaz define una sobrecarga de ToString con los parámetros formaty formatProvider. FormatProvider le ayuda a formatear el texto de una manera consciente de la cultura.

Consulte también: Object.ToString e IFormattable

jbe
fuente
0

Más simple depende de cómo se vaya a utilizar su propiedad. Si solo necesita formatear la cadena una vez, no tiene mucho sentido anularla.

Sin embargo, parece que está anulando el método ToString para no devolver los datos de cadena normales para su propiedad, sino para realizar un patrón de formato estándar. Dado que está utilizando string.format con padding.

Debido a que dijo que está aprendiendo, el ejercicio también parece afectar los principios básicos de la programación orientada a objetos relacionados con la encapsulación y la reutilización del código.

El string.format que toma los argumentos que ha establecido para el relleno asegura que la propiedad se formateará de la misma manera cada vez para cualquier código que la llame. Además, en el futuro, solo tendrá que cambiarlo en un lugar en lugar de en muchos.

¡Gran pregunta y también grandes respuestas!

SoftwareCarpintero
fuente
0

Encuentro útil anular el método ToString en las clases de entidad, ya que ayuda a identificar rápidamente los problemas en las pruebas, especialmente cuando una afirmación falla, la consola de prueba invocará el método ToString en el objeto.

Pero de acuerdo con lo que se ha dicho antes, se trata de dar una representación legible por humanos del objeto en cuestión.

ranjez
fuente
0

Estoy satisfecho con las Directrices del marco mencionadas en otras respuestas. Sin embargo, me gustaría enfatizar los propósitos de visualización y depuración.

Tenga cuidado con cómo lo usa ToStringen su código. Su código no debería depender de la representación de cadena de un objeto. Si es así, debe proporcionar los Parsemétodos respectivos .

Dado que ToStringse puede usar en todas partes, puede ser un problema de mantenimiento si desea cambiar la representación de cadena de un objeto en un momento posterior. No puede simplemente examinar la jerarquía de llamadas en este caso para estudiar si algún código se romperá.

tm1
fuente