Por que / quando seria apropriado substituir ToString?

103

Estou estudando C # e me pergunto qual ToStringpode ser o objetivo e a vantagem de substituir , conforme mostrado no exemplo abaixo.

Isso poderia ser feito de uma maneira mais simples, usando um método comum sem a substituição?

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
fonte
4
Depende se você realmente deseja usá-lo para exibir o objeto na IU em qualquer lugar, caso contrário, geralmente é apenas para depuração - você verá a saída de ToString na janela de observação do VS. (Mas você também pode conseguir isso com atributos na classe.) Dado que você tem títulos e saída, parece que este programa está usando para despejar o objeto usando Console.WriteLine(obj.GetToStringItemsHeadings); Console.WriteLine(obj);ou semelhante.
Rup
2
Eu realmente não entendo a pergunta. Isso pode ser feito de forma diferente? Sim. Mas por que você quer fazer de forma diferente.
Konrad Rudolph
5
Eu realmente pularia GetToStringno nome da propriedade ... Então você obteria, por exemploConsole.WriteLine(obj.ItemHeadings)
Svish
e altere a propriedade ItemHeadings para estática, pois não precisa de nenhum "this".
eFloh
Você pode querer verificar esta questão: stackoverflow.com/questions/7906828/oo-design-advice-tostring
Yuri Ghensev

Respostas:

127
  • Você precisa substituir ToString? Não.

  • Você pode obter uma representação de string de seu objeto de outra maneira? Sim.

Mas, ao usar, ToStringvocê está usando um método comum a todos os objetos e, portanto, outras classes sabem sobre esse método. Por exemplo, sempre que o .NET framework deseja converter um objeto em uma representação de string, ToStringé um candidato principal (existem outros, se você quiser fornecer opções de formatação mais elaboradas).

Concretamente,

Console.WriteLine(yourObject);

iria invocar yourObject.ToString().

Konrad Rudolph
fonte
13
Sim. Também é muito útil no desenvolvimento MVC ou ASP.Net onde @yourObjecte <%=yourObject%>será "ToString-ed".
Ben Lesh
4
Além disso, ao preencher uma caixa de combinação, ToString é a chamada padrão para obter o texto do item
Davi Fiamenghi
3
Cuidado com essa prática. História verídica: um aluno substituiu ToString por um método que, além de retornar uma string, alterou os dados. Ele estava depurando o programa, mas enquanto o programa estava em um ponto de interrupção, o valor continuava mudando. Aparentemente - toda vez que ele verificava o valor ToString era executado - o valor mudava mesmo que o programa não estivesse em execução.
JNF
3
@JNF O que é “tal prática”? ToStringnão deve realmente alterar o estado de um objeto. Mas não foi isso que eu defendi.
Konrad Rudolph
8
@JNF Não aceito isso. ToStringestá destinado a ser substituído. Período. Colocar uma advertência nesta declaração não é produtivo. Se você fizer isso errado - sua própria culpa. Na verdade, seu aluno violou o ponto 4 da lista oficial de diretrizes para anulação ToStringque foi postada na resposta de David Anderson.
Konrad Rudolph
129

Vou apenas dar a você a resposta direto do Framework Design Guidelines da .NET Development Series.

EVITE lançar exceções deToString

CONSIDER retornando uma string exclusiva associada à instância.

CONSIDERE ter a saída de ToStringser uma entrada válida para qualquer método de análise neste tipo.

ASSEGURE- SE de que ToStringnão haja efeitos colaterais observáveis.

DO relatório de informação sensível à segurança através de uma substituição de ToStringapenas após exigindo uma permissão apropriada. Se a solicitação de permissão falhar, retorne uma string excluindo informações confidenciais de segurança.

O Object.ToStringmétodo deve ser usado para fins gerais de exibição e depuração. A implementação padrão simplesmente fornece o nome do tipo de objeto. A implementação padrão não é muito útil e é recomendado que o método seja substituído.

DO substituição ToStringsempre que uma string legível interessante pode ser devolvido. A implementação padrão não é muito útil e uma implementação customizada quase sempre pode fornecer mais valor.

Prefira um nome amigável em vez de um ID exclusivo, mas não legível.

Também vale a pena mencionar, já que Chris Sells também explica nas diretrizes, que ToStringgeralmente é perigoso para interfaces de usuário. Geralmente, minha regra prática é expor uma propriedade que seria usada para vincular informações à IU e deixar a ToStringsubstituição para exibir informações de diagnóstico para o desenvolvedor. Você também pode decorar seu tipo com DebuggerDisplayAttribute.

DO tentar manter a cadeia devolvida a partir ToStringcurto. O depurador usa ToStringpara obter uma representação textual de um objeto a ser mostrado ao desenvolvedor. Se a string for mais longa do que o depurador pode exibir, a experiência de depuração é prejudicada.

FAÇA a formatação da string com base na cultura de thread atual ao retornar informações dependentes da cultura.

DO fornecer sobrecarga ToString(string format), ou implementar IFormattable, se o retorno seqüência de ToStringé sensível à cultura ou existem várias maneiras de formatar a string. Por exemplo, DateTimefornece a sobrecarga e implementos IFormattable.

NÃO retorne uma string vazia ou nula deToString

Juro por essas diretrizes e você deve fazer isso. Eu não posso dizer como meu código melhorou apenas por esta diretriz para ToString. A mesma coisa vale para coisas como IEquatable(Of T)e IComparable(Of T). Essas coisas tornam seu código muito funcional e você não se arrependerá de perder tempo para implementá-lo.

Pessoalmente, nunca usei ToString muito para interfaces de usuário, sempre expus uma propriedade ou método de algum tipo. Na maioria das vezes, você deve usar ToStringpara fins de depuração e desenvolvedor. Use-o para exibir informações importantes de diagnóstico.

David Anderson
fonte
6
Boa resposta. Essas são as diretrizes às quais você se refere msdn.microsoft.com/en-us/library/ms229042.aspx ?
Jodrell
2
@Jodrell, acredito que foi tirado das Diretrizes de Design
Jim Schubert,
6
Melhor citação necessária.
Ben Voigt
13
Eu não sabia que o SO estava se transformando em Wikipedia.
David Anderson,
14
Não é, mas quando você cita diretrizes, fornecer a fonte é uma vantagem indiscutível.
Falanwe
39

A substituição ToString()permite que você forneça uma representação de string legível por humanos de uma classe.

Isso significa que a saída pode revelar informações úteis sobre sua classe. Por exemplo, se você tivesse uma classe Person, você poderia escolher ter como ToString()saída o id da pessoa, seu nome, sobrenome, etc. Isso é extremamente útil ao depurar ou registrar.

Com relação ao seu exemplo - é difícil dizer se sua substituição é útil sem saber o que esta classe é - mas a implementação em si está ok.

Rob Levine
fonte
13

É sempre apropriado, mas considere cuidadosamente as intenções por trás do que você está exibindo

Uma pergunta melhor seria fazer:

Por que alguém substituiria ToString ()?

ToString () é a janela para o estado de um objeto. Ênfase no estado como um requisito. Linguagens fortemente OOP como Java / C # abusam do modelo OOP encapsulando tudo em uma classe. Imagine que você está codificando em uma linguagem que não segue o modelo OOP forte; considere se você usaria uma classe ou uma função. Se você for usá-lo como uma função (ou seja, verbo, ação) e o estado interno for mantido apenas temporariamente entre a entrada / saída, ToString () não adicionará valor.

Como outros mencionaram, é importante considerar o que você produz com ToString () porque ele pode ser usado pelo depurador ou outros sistemas.

Gosto de imaginar o método ToString como o parâmetro --help de um objeto. Deve ser curto, legível, óbvio e fácil de exibir. Deve mostrar o que o objeto não é o que faz . Com tudo isso em mente, vamos considerar ...

Caso de uso - analisando um pacote TCP:

Não é uma captura de rede apenas no nível do aplicativo, mas algo com mais carne, como uma captura pcap.

Você deseja sobrecarregar ToString () apenas para a camada TCP para que possa imprimir dados no console. O que isso incluiria? Você poderia enlouquecer e analisar todos os detalhes do TCP (ou seja, o TCP é complexo) ...

Que inclui:

  • Porta Fonte
  • Porto de destino
  • Número sequencial
  • Número de confirmação
  • Deslocamento de dados
  • Bandeiras
  • Deslocamento da janela
  • Checksum
  • Ponteiro Urgente
  • Opções (eu nem vou lá)

Mas você gostaria de receber todo esse lixo se estivesse chamando TCP.ToString () em 100 pacotes? Claro que não, seria sobrecarga de informação. A escolha fácil e óbvia também é a mais sensata ...

Exponha o que as pessoas esperariam ver:

  • Porta Fonte
  • Porto de destino

Eu prefiro uma saída sensata que seja fácil para humanos analisar, mas YMMV .

TCP:[destination:000, source:000]

Nada complexo, a saída não é para ser analisada por máquinas (ou seja, a menos que as pessoas estejam abusando do seu código), a finalidade pretendida é para legibilidade humana.

Mas e todo o resto daquela informação suculenta de que falei antes, não é útil também? Vou chegar lá, mas primeiro ...


ToString () um dos métodos mais valiosos e subutilizados de todos os tempos

Por dois motivos:

  1. As pessoas não entendem para que serve ToString ()
  2. A classe base 'Object' não tem outro método de string igualmente importante.

Razão 1 - Não abuse da utilidade de ToString ():

Muitas pessoas usam ToString () para obter uma representação de string simples de um objeto. O manual C # ainda afirma:

ToString é o principal método de formatação no .NET Framework. Ele converte um objeto em sua representação de string para que seja adequado para exibição.

Exibição, sem processamento adicional. Isso não significa, pegue minha bela representação de string do pacote TCP acima e puxe a porta de origem usando um regex :: cringe ::.

A maneira certa de fazer as coisas é chamar ToString () diretamente na propriedade SourcePort (que BTW é um ushort, então ToString () já deve estar disponível).

Se você precisa de algo mais robusto para empacotar o estado de um objeto complexo para análise de máquina, será melhor usar uma estratégia de serialização estruturada.

Felizmente, essas estratégias são muito comuns:

  • ISerializable (C #)
  • Pickle (Python)
  • JSON (Javascript ou qualquer linguagem que o implemente)
  • SABONETE
  • etc ...

Nota: A menos que você esteja usando PHP porque, herp-derp, há uma função para isso :: snicker ::

Razão 2 - ToString () não é suficiente:

Ainda estou para ver uma linguagem que implemente isso no núcleo, mas tenho visto e usado variações dessa abordagem na selva.

Algumas das quais incluem:

  • ToVerboseString ()
  • ToString (verbose = true)

Basicamente, essa bagunça cabeluda do estado de um pacote TCP deve ser descrita para facilitar a leitura humana. Para evitar 'bater em um cavalo morto' falando sobre TCP, irei 'apontar o dedo' para o caso nº 1 onde eu acho que ToString () e ToVerboseString () são subutilizados ...

Caso de uso - Arrays:

Se você usa principalmente um idioma, provavelmente está confortável com a abordagem desse idioma. Para pessoas como eu, que alternam entre idiomas diferentes, o número de abordagens variadas pode ser irritante.

Ou seja, o número de vezes que isso me irritou é maior do que a soma de todos os dedos de todos os deuses hindus combinados.

vários casos em que línguas uso comum hacks e um poucos que obter -lo direito . Alguns exigem a reinvenção da roda, alguns fazem um despejo raso, outros fazem um despejo profundo, nenhum deles funciona da maneira que eu gostaria ...

O que estou pedindo é uma abordagem muito simples:

print(array.ToString());

Saídas: 'Array [x]' ou 'Array [x] [y]'

Onde x é o número de itens na primeira dimensão ey é o número de itens na segunda dimensão ou algum valor que indica que a 2ª dimensão é denteada (intervalo mínimo / máximo talvez?).

E:

print(array.ToVerboseString());

Produz todo o she-bang em letras bonitas porque eu aprecio coisas bonitas.

Espero que isso lance alguma luz sobre um tópico que me incomoda há muito tempo. No mínimo, espalhei uma pequena isca de troll para que os PHPers votassem negativamente nessa resposta.

Evan Solha
fonte
Você escreveu: "Ainda estou para ver uma linguagem que implemente isso no núcleo". Parece-me que Python chega bem perto disso; por exemplo, imprimir uma matriz fornece todos os seus dados de forma concisa. Isso é algo que sinto falta em C # / Java, embora goste de seu rigor. E quando você precisar ir um pouco além de ToString (), seria bom ter algo como a sintaxe de compreensão de lista do Python; Acho que string.Join () é semelhante, embora menos flexível.
Jon Coombs
1
@JCoombs Não discordo, as compreensões são ótimas. Percorrer estruturas de dados em Python por meio de compreensões torna a vida muito mais fácil, mas essa abordagem ainda requer conhecimento íntimo da estrutura interna do objeto. Algo que nem sempre é óbvio com classes que foram importadas de uma biblioteca externa. É uma boa prática para os autores da biblioteca sobrescrever ToString () e fornecer uma representação legível útil, mas também seria bom ter um método de dump geral que produza a estrutura do objeto em um formato legível por humanos estruturado geral (ex JSON).
Evan Plaice
Excelente ponto. Portanto, em vez de mostrar nada mais do que basicamente um typeof, ele pode se auto-serializar para algo como JSON. (Até agora, eu só vi objetos .NET serializados para XML, mas sou novo no .NET) Mas então o perigo pode ser a tentação de tratar ToString () como legível por máquina? (Por exemplo, espera ser capaz de desserializá-lo.)
Jon Coombs
1
@JCoombs Sim, em um nível funcional, JSON e XML têm o mesmo propósito. Muitas pessoas usam ToString como uma saída legível por máquina; se isso é ruim ou não, é discutível. Minha maior queixa é - muitas vezes - é uma dor de saída do estado de um objeto em um formato legível por humanos. A implementação de ToString () é freqüentemente subutilizada e ler o estado de um objeto fora das listas de observação do depurador é uma dor. As representações serializadas são boas como backup para exibir o estado inteiro de um objeto, mas melhores implementações de ToString são a melhor opção.
Evan Plaice
Se ToString()for para depuração, por que é a única maneira de extrair todo o texto de um StringBuilder - uma função importante?
Dan W
9

É sobre boas práticas tanto quanto qualquer outra coisa, na verdade.

ToString()é usado em muitos lugares para retornar uma representação de string de um objeto, geralmente para consumo por um ser humano. Freqüentemente, essa mesma string pode ser usada para reidratar o objeto (pense em intou DateTimepor exemplo), mas isso nem sempre é dado (uma árvore, por exemplo, pode ter uma representação de string útil que simplesmente exibe um Count, mas claramente você não pode usar isso para reconstruí-lo).

Em particular, o depurador usará isso para exibir a variável nas janelas de observação, janelas imediatas, etc., portanto, ToStringé realmente inestimável para depuração.

Em geral, também, esse tipo geralmente terá um membro explícito que também retorna uma string. Por exemplo, um par Lat / Long pode ter ToDecimalDegreesquais retornos, "-10, 50"por exemplo, mas também pode ter ToDegreesMinutesSeconds, já que esse é outro formato para um par Lat / Long. Esse mesmo tipo também pode substituir ToStringpor um daqueles para fornecer um 'padrão' para coisas como depuração, ou mesmo potencialmente para coisas como renderização de páginas da web (por exemplo, a @construção no Razor escreve o ToString()resultado de uma expressão não string para o fluxo de saída).

Andras Zoltan
fonte
6

object.ToString()converte um objeto em sua representação de string. Se você deseja alterar o que é retornado quando um usuário chama ToString()uma classe que você criou, você precisa substituir ToString()essa classe.

Robbie
fonte
6

Embora eu ache que as informações mais úteis já foram fornecidas, devo acrescentar meus dois centavos:

  • ToString()deve ser substituído. Sua implementação padrão retorna o nome do tipo que, embora possa ser útil às vezes (particularmente ao trabalhar com muitos objetos), não é suficiente na grande maioria das vezes.

  • Lembre-se de que, para fins de depuração, você pode confiar DebuggerDisplayAttribute. Você pode ler mais sobre isso aqui .

  • Como regra, em POCOs você sempre pode substituir ToString(). POCOs são uma representação estruturada de dados, que geralmente podem se tornar uma string.

  • Projete ToString para ser uma representação textual de seu objeto. Talvez seus principais campos e dados, talvez uma descrição de quantos itens estão na coleção, etc.

  • Sempre tente encaixar essa string em uma única linha e tenha apenas as informações essenciais. Se você tiver uma Personclasse com as propriedades Nome, Endereço, Número etc., retorne apenas os dados principais (Nomeie algum número de ID).

  • Tenha cuidado para não substituir uma boa implementação de ToString(). Algumas classes do framework já implementam ToString(). Substituir essa implementação padrão é uma coisa ruim: as pessoas esperarão um certo resultado ToString()e obterão outro.

Não tenha medo de usar ToString(). A única coisa com que eu teria cuidado é retornar informações confidenciais. Fora isso, o risco é mínimo. Claro, como alguns apontaram, outras classes usarão seu ToString sempre que buscar informações. Mas, diabos, quando retornar o nome do tipo será considerado melhor do que obter algumas informações reais?

Bruno Brant
fonte
5

Se você não substituir ToString, obterá a implementação de suas classes base que, pois, Objecté apenas o nome de tipo abreviado da classe.

Se você quiser alguma outra implementação mais significativa ou útil do ToString, substitua-o.


Isso pode ser útil ao usar uma lista do seu tipo como a fonte de dados para a, ListBoxpois ToStringserá exibida automaticamente.

Outra situação ocorre quando você deseja passar seu tipo para o String.Formatqual invoca ToStringpara obter uma representação de seu tipo.

Jodrell
fonte
4

Algo que ninguém mais mencionou ainda: ao substituir ToString(), você também pode considerar a implementação IFormattablepara fazer o seguinte:

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

O que pode ser útil.

Zhaph - Ben Duguid
fonte
Você pode dar um exemplo de uma classe de framwork fornecendo esse método para substituir? A única coisa relacionada que conheço na estrutura é a IFormattibleinterface.
tm1
@ tm1 Qualquer objeto que herde de System.Objectterá um ToString()método substituível , mas você está certo que o método do formatador não deve ser overridee deve realmente fazer referência à IFormattibleinterface para conformidade completa.
Zhaph - Ben Duguid
Quase - IFormattableleva um IFormatProviderparâmetro. Obrigado por editar sua postagem.
tm1
Na verdade, ele tem dois métodos, o valor que eu estava expressando estava no mostrado;)
Zhaph - Ben Duguid
3

Um benefício de substituir ToString () é o suporte a ferramentas do Resharper: Alt + Ins -> "Formatando membros" e grava o ToString () para você.

Daniel James Bryars
fonte
2

Em alguns casos, torna mais fácil ler os valores das classes personalizadas na janela de observação do depurador. Se eu sei exatamente o que quero ver na janela de observação, quando substituo ToString com essa informação, então eu vejo.

filólogo
fonte
1

Ao definir structs (efetivamente primitivos do usuário), acho que é uma boa prática ter métodos, ToStringe Parsee correspondentes TryParse, especialmente para serialização XML. Neste caso, você converterá todo o estado em uma string, para que possa ser lido posteriormente.

As classes, entretanto, são estruturas mais compostas que geralmente são muito complexas para usar ToStringe Parse. Seus ToStringmétodos, em vez de salvar todo o estado, podem ser uma descrição simples que ajuda a identificar seu estado, como um identificador único como um nome ou ID, ou talvez uma quantidade para uma lista.

Além disso, como Robbie disse, substituir ToStringpermite que você chame ToStringuma referência tão básica quanto o tipo object.

Dave Cousineau
fonte
1

Você pode usar isso quando tiver um objeto sem significado intuitivo de representação de string, como pessoa. Então, se você precisa, por exemplo, imprimir esta pessoa, você pode usar esta substituição para preparar seu formato.

NickF
fonte
1

O Object.ToStringmétodo deve ser usado apenas para fins de depuração. A implementação padrão mostra o nome do tipo de objeto que não é muito útil. Considere substituir esse método para fornecer melhores informações para diagnóstico e depuração. Considere que as infraestruturas de registro geralmente também usam o método ToString e, portanto, você encontrará esses fragmentos de texto em seus arquivos de registro.

Não retorne recursos de texto localizados dentro do Object.ToStringmétodo. O motivo é que o método ToString deve sempre retornar algo que o desenvolvedor possa entender. O desenvolvedor pode não falar todos os idiomas suportados pelo aplicativo.

Implemente a IFormattableinterface quando desejar retornar um texto localizado amigável. Esta interface define uma sobrecarga ToString com os parâmetros formate formatProvider. O formatProvider ajuda você a formatar o texto de uma maneira que leve em consideração a cultura.

Veja também: Object.ToString e IFormattable

jbe
fonte
0

Mais simples depende de como sua propriedade será usada. Se você só precisa formatar a string uma vez, não faz muito sentido substituí-la.

No entanto, parece que você está substituindo o método ToString para não retornar os dados de string normais para sua propriedade, mas para executar um padrão de formatação padrão. Já que você está usando string.format com preenchimento.

Como você disse que está aprendendo, o exercício parece também atingir os princípios básicos da programação orientada a objetos relacionados ao encapsulamento e à reutilização de código.

O string.format recebendo os argumentos que você definiu para preenchimento garante que a propriedade será formatada da mesma maneira todas as vezes para qualquer código que a chame. Da mesma forma, daqui para frente, você só precisa alterá-lo em um lugar, em vez de muitos.

Ótima pergunta e também ótimas respostas!

SoftwareCarpenter
fonte
0

Acho útil substituir o método ToString nas classes de entidade, pois ajuda a identificar rapidamente os problemas no teste, especialmente quando uma asserção falha, o console de teste invoca o método ToString no objeto.

Mas, de acordo com o que foi dito antes, é para dar uma representação legível humana do objeto em questão.

Ranjez
fonte
0

Estou satisfeito com as Diretrizes da Estrutura mencionadas em outras respostas. No entanto, gostaria de enfatizar os propósitos de exibição e depuração.

Seja cuidadoso sobre como você usa ToStringem seu código. Seu código não deve depender da representação de string de um objeto. Em caso afirmativo, você deve fornecer os respectivos Parsemétodos.

Uma vez que ToStringpode ser usado em qualquer lugar, pode ser um problema de manutenção se você quiser alterar a representação de string de um objeto posteriormente. Você não pode simplesmente examinar a hierarquia de chamadas neste caso para estudar se algum código será quebrado.

tm1
fonte