web-dev-qa-db-fra.com

Pourquoi/quand serait-il approprié de remplacer ToString?

J'étudie C # et je me demande quel pourrait être l'intérêt et l'avantage de remplacer ToString, comme le montre l'exemple ci-dessous.

Cela pourrait-il être fait d'une manière plus simple, en utilisant une méthode commune sans substitution?

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;
}
99
3D-kreativ
  • Avez-vous besoin de remplacer ToString? Non.

  • Pouvez-vous obtenir une représentation sous forme de chaîne de votre objet d'une autre manière? Oui.

Mais en utilisant ToString, vous utilisez une méthode commune à tous les objets, ce qui permet aux autres classes de connaître cette méthode. Par exemple, lorsque le framework .NET souhaite convertir un objet en une représentation sous forme de chaîne, ToString est un candidat idéal (il en existe d'autres, si vous souhaitez fournir des options de formatage plus élaborées).

Concrètement 

Console.WriteLine(yourObject);

invoquerait yourObject.ToString().

126
Konrad Rudolph

Je vais simplement vous donner la réponse directement à partir de Framework Design Guidelines à partir de .NET Development Series. 

EVITER levant des exceptions de ToString

CONSIDER retournant une chaîne unique associée à l'instance.

CONSIDER dont le résultat ToString est une entrée valide pour toutes les méthodes d'analyse de ce type.

DO _ s'assurent que ToString n'a pas d'effets secondaires observables.

DO signale des informations confidentielles relatives à la sécurité en remplaçant ToString uniquement après avoir demandé une autorisation appropriée. Si la demande d'autorisation échoue, renvoyez une chaîne excluant les informations confidentielles.

La méthode Object.ToString est destinée à être utilisée à des fins générales d’affichage et de débogage. L'implémentation par défaut fournit simplement le nom du type d'objet. L'implémentation par défaut n'est pas très utile et il est recommandé de remplacer la méthode.

DO substitue ToString chaque fois qu'une chaîne lisible par l'homme peut être retournée. L'implémentation par défaut n'est pas très utile et une implémentation personnalisée peut presque toujours fournir plus de valeur. 

DO préfèrent un nom convivial à un ID unique mais non lisible. 

Il convient également de mentionner que Chris Sells explique également dans les directives que ToString est souvent dangereux pour les interfaces utilisateur. En règle générale, ma règle empirique consiste à exposer une propriété qui serait utilisée pour lier des informations à l'interface utilisateur et à laisser le remplacement ToString pour l'affichage des informations de diagnostic au développeur. Vous pouvez également décorer votre type avec DebuggerDisplayAttribute.

DO essaie de garder la chaîne retournée de ToString. Le débogueur utilise ToString pour obtenir une représentation textuelle d'un objet à montrer au développeur. Si la chaîne est plus longue que ce que le débogueur peut afficher, l'expérience de débogage est compromise.

DO formatage de chaîne basé sur la culture de thread actuelle lors du renvoi d'informations dépendantes de la culture.

DO surcharge ToString(string format), ou implémente IFormattable, si le retour de chaîne de ToString est sensible à la culture ou s'il existe différentes façons de formater la chaîne. Par exemple, DateTime fournit la surcharge et implémente IFormattable

NE PAS FAIRE retourne une chaîne vide ou null de ToString

Je jure par ces directives, et vous devriez le faire. Je ne peux pas vous dire comment mon code s'est amélioré uniquement grâce à cette directive pour ToString. La même chose vaut pour des choses comme IEquatable(Of T) et IComparable(Of T). Ces éléments rendent votre code très fonctionnel et vous ne regretterez pas d'avoir pris le temps supplémentaire pour le mettre en œuvre.

Personnellement, je n'ai jamais vraiment utilisé ToStringbeaucoup pour les interfaces utilisateur, j'ai toujours exposé une propriété ou une méthode quelconque. La plupart du temps, vous devez utiliser ToString à des fins de débogage et de développement. Utilisez-le pour afficher des informations de diagnostic importantes.

127
David Anderson

Remplacer ToString() vous permet de donner une représentation sous forme de chaîne lisible par l’homme d’une classe.

Cela signifie que la sortie peut révéler des informations utiles sur votre classe. Par exemple, si vous avez une classe Person, vous pouvez choisir que la fonction ToString() fournisse l'id de la personne, son prénom, son nom, etc. Ceci est extrêmement utile lors du débogage ou de la journalisation.

En ce qui concerne votre exemple - il est difficile de dire si votre substitution est utile sans savoir ce que cette classe est - mais l'implémentation elle-même est correcte.

39
Rob Levine

C'est toujours approprié mais réfléchissez bien aux intentions derrière ce que vous affichez

Une meilleure question serait de demander:

Pourquoi voudrait-on remplacer ToString ()?

ToString () est la fenêtre dans l'état d'un objet. L'accent est mis sur state comme condition requise. Les langages fortement OOP tels que Java/C # abusent du modèle OOP en encapsulant tout dans une classe. Imaginez que vous codiez dans un langage qui ne suit pas le modèle fort OOP; Déterminez si vous utiliseriez une classe ou une fonction. Si vous l'utilisiez comme fonction (par exemple, verbe, action) et que l'état interne n'est maintenu que temporairement entre les entrées/sorties, ToString () n'ajoutera pas de valeur.

Comme d'autres l'ont mentionné, il est important de considérer what vous exportez avec ToString () car il pourrait être utilisé par le débogueur ou d'autres systèmes.

J'aime imaginer la méthode ToString comme paramètre --help d'un objet. Il doit être court, lisible, évident et facile à afficher. Il devrait afficher ce que l'objet est et non ce qu'il fait . Avec tout cela à l'esprit, considérons ...

Cas d'utilisation - Analyse d'un paquet TCP:

Ce n'est pas une capture réseau au niveau de l'application mais quelque chose avec plus de viande qu'une capture pcap.

Vous souhaitez surcharger ToString () uniquement pour la couche TCP afin que vous puissiez imprimer des données sur la console. Que comprendrait-il? Vous pouvez devenir fou et analyser tous les détails de TCP (c'est-à-dire que TCP est complexe) ...

Qui comprend le:

  • Port source
  • Le port de destination
  • Numéro de séquence
  • Numéro d'accusé de réception
  • Décalage de données
  • Drapeaux
  • Décalage de fenêtre
  • Somme de contrôle
  • Pointeur urgent
  • Options (je ne vais pas même y aller)

Mais voudriez-vous recevoir tout ce courrier indésirable si vous appeliez TCP.ToString () sur 100 paquets? Bien sûr que non, ce serait une surcharge d'informations. Le choix facile et évident est aussi le plus judicieux ...

Exposez ce que les gens s’attendraient à voir:

  • Port source
  • Le port de destination

Je préfère une sortie sensible, facile à analyser pour les humains, mais YMMV .

TCP:[destination:000, source:000]

Rien de complexe, la sortie n'est pas analysable par les machines (sauf si les utilisateurs abusent de votre code), le but recherché est la lisibilité.

Mais qu'en est-il de toutes les informations juteuses dont j'ai parlé auparavant, cela n'est-il pas utile aussi? J'y viendrai mais d'abord ...


ToString () l’une des méthodes les plus précieuses et les moins utilisées de tous les temps

Pour deux raisons:

  1. Les gens ne comprennent pas à quoi sert ToString ()
  2. La classe de base 'Object' manque d'une autre méthode de chaîne d'égale importance.

Reason 1 - N'abusez pas de l'utilité de ToString ():

Beaucoup de gens utilisent ToString () pour tirer une représentation sous forme de chaîne simple d'un objet. Le manuel C # dit même:

ToString est la principale méthode de formatage du .NET Framework. Il convertit un objet en sa représentation sous forme de chaîne afin qu'il soit adapté à l'affichage.

Display, pas de traitement ultérieur. Cela ne signifie pas, prenez ma représentation Nice du paquet TCP ci-dessus et extrayez le port source à l'aide d'un regex :: cringe ::.

La manière right == de faire les choses est, appelez ToString () directement sur la propriété SourcePort (BTW est un ushort donc ToString () devrait déjà être disponible).

Si vous avez besoin de quelque chose de plus robuste pour conditionner l'état d'un objet complexe pour l'analyse de la machine, utilisez une stratégie de sérialisation structurée.

Heureusement, ces stratégies sont très courantes:

  • ISerializable (C #)
  • Pickle (Python)
  • JSON (Javascript ou n'importe quel langage qui l'implémente)
  • SOAP
  • etc...

Remarque: à moins que vous n'utilisiez PHP car, herp-derp, il existe une fonction pour cela :: snicker ::

Reason 2 - ToString () n'est pas suffisant:

Je n'ai pas encore vu de langage implémentant cela à la base, mais j'ai vu et utilisé des variantes de cette approche dans la nature.

Certains d'entre eux comprennent:

  • ToVerboseString ()
  • ToString (verbose = true)

Fondamentalement, ce désordre poilu d'un TCP état de paquet devrait être décrit pour la lisibilité humaine. Pour éviter de "frapper un cheval mort" en parlant de TCP, je vais pointer du doigt le cas # 1 où je pense que ToString () et ToVerboseString () sont sous-utilisés ...

Cas d’utilisation - Tableaux:

Si vous utilisez principalement une langue, vous êtes probablement à l'aise avec l'approche de cette langue. Pour les personnes comme moi qui sautent d'une langue à l'autre, le nombre d'approches variées peut être irritant.

C'est-à-dire que le nombre de fois que cela m'a irrité est plus grand que la somme de tous les doigts de chaque dieu hindou combiné.

Il y a divers cas où les langues utilisent common hacks et un pe that get it right =. Certains requièrent une réinvention de la roue, certains font un dépotoir peu profond, d'autres un dépotoir profond, aucun d'entre eux ne fonctionne comme je le souhaiterais ...

Ce que je demande, c’est une approche très simple:

print(array.ToString());

Sorties: 'Tableau [x]' ou 'Tableau [x] [y]'

Où x est le nombre d'éléments de la première dimension et y le nombre d'éléments de la deuxième dimension ou une valeur indiquant que la deuxième dimension est dentelée (plage min/max peut-être?).

Et:

print(array.ToVerboseString());

Reproduit toute la she-bang en jolie impression parce que j'apprécie les belles choses.

Espérons que cela jette un peu de lumière sur un sujet qui me dérange depuis longtemps. À tout le moins, j’ai saupoudré un peu d’appât pour les PHPers afin d’abaisser cette réponse.

13
Evan Plaice

Il s’agit de bonnes pratiques autant que d’autre chose.

ToString() est utilisé dans de nombreux endroits pour renvoyer une représentation sous forme de chaîne d'un objet, généralement pour la consommation par un humain. Souvent, cette même chaîne peut être utilisée pour réhydrater l'objet (pensez à int ou DateTime par exemple), mais ce n'est pas toujours une donnée (un arbre par exemple pourrait avoir une représentation de chaîne utile qui affiche simplement un compte, mais vous pouvez clairement ' ne l’utilisez pas pour le reconstruire).

En particulier, le débogueur l'utilisera pour afficher la variable dans les fenêtres de surveillance, les fenêtres immédiates, etc. De ce fait, ToString est réellement inestimable pour le débogage.

En général, un tel type aura souvent un membre explicite qui renvoie également une chaîne. Par exemple, une paire Lat/Long peut avoir ToDecimalDegrees qui renvoie "-10, 50" par exemple, mais elle peut également avoir ToDegreesMinutesSeconds, car il s'agit d'un autre format pour une paire Lat/Long. Ce même type peut alors également remplacer ToString par l’un de ceux-ci afin de fournir un 'paramètre par défaut' pour des opérations telles que le débogage ou potentiellement pour le rendu de pages Web (par exemple, la construction @ dans Razor écrit le résultat ToString() d’une expression dans le flux de sortie).

9
Andras Zoltan

Bien que je pense que les informations les plus utiles ont déjà été fournies, je vais ajouter mes deux centimes:

  • ToString() est destiné à être remplacé. Son implémentation par défaut renvoie le nom du type qui, bien que utile parfois (en particulier lorsque vous travaillez avec beaucoup d'objets), ne suffit pas dans la plupart des cas.

  • N'oubliez pas que, pour le débogage, vous pouvez compter sur DebuggerDisplayAttribute. Vous pouvez en lire plus à ce sujet ici

  • En règle générale, sur les POCO, vous pouvez toujours remplacer ToString(). Les POCO sont une représentation structurée des données, qui peuvent généralement devenir une chaîne.

  • Concevez ToString pour être une représentation textuelle de votre objet. Peut-être ses principaux champs et données, peut-être une description du nombre d’éléments contenus dans la collection, etc.

  • Essayez toujours de faire correspondre cette chaîne à une seule ligne et ne disposez que des informations essentielles. Si vous avez une classe Person avec les propriétés Name, Address, Number etc., renvoyez uniquement les données principales (Nommez un numéro d'identification). 

  • Veillez à ne pas ignorer une bonne implémentation de ToString(). Certaines classes d'infrastructure implémentent déjà ToString(). Négliger cette implémentation par défaut est une mauvaise chose: les gens s'attendent à un certain résultat de ToString() et en obtiennent un autre. 

N'ayez pas vraiment peur d'utiliser ToString(). La seule chose à laquelle je ferais attention est de renvoyer des informations sensibles. Autre que cela, le risque est minime. Bien sûr, comme certains l’ont souligné, d’autres classes utiliseront votre ToString chaque fois qu’elles recherchent des informations. Mais bon, quand est-il préférable de renvoyer le nom du type que d'obtenir des informations réelles?

6
Bruno Brant

object.ToString() convertit un objet en sa représentation sous forme de chaîne. Si vous souhaitez modifier ce qui est renvoyé lorsqu'un utilisateur appelle ToString() sur une classe que vous avez créée, vous devez remplacer ToString() dans cette classe.

6
Robbie

Si vous ne remplacez pas ToString, vous obtenez alors votre implémentation de classes de base qui, pour Object, est simplement le nom de type abrégé de la classe.

Si vous souhaitez une autre implémentation plus significative ou plus utile de ToString, remplacez-la.


Cela peut être utile lorsque vous utilisez une liste de votre type en tant que source de données pour une ListBox car la ToString sera automatiquement affichée.

Une autre situation se produit lorsque vous voulez passer votre type à String.Format qui appelle ToString pour obtenir une représentation de votre type.

5
Jodrell

Quelque chose que personne d’autre n’a encore mentionné: en remplaçant ToString(), vous pouvez également envisager de remplacer ToString(string Formatter) pour pouvoir:

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

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

Ce qui peut être utile.

4

L'un des avantages de la substitution de ToString () est la prise en charge de l'outillage de Resharper: Alt + Ins -> "Formatage des membres" et écrit la tâche ToString () à votre place.

3

Dans certains cas, cela facilite la lecture des valeurs des classes personnalisées dans la fenêtre de surveillance du débogueur. Si je sais exactement ce que je veux voir dans la fenêtre de surveillance, lorsque je remplace ToString par cette information, je la vois.

2
philologon

Lors de la définition des structures (primitives utilisateur), je trouve que les méthodes ToString, Parse et TryParse doivent correspondre, en particulier pour la sérialisation XML. Dans ce cas, vous convertissez tout l'état en une chaîne, de sorte qu'elle puisse être lue ultérieurement.

Les classes sont cependant des structures plus composées qui seront généralement trop complexes pour utiliser ToString et Parse. Leurs méthodes ToString, au lieu de sauvegarder l’intégralité de l’état, peuvent être une description simple qui vous aide à identifier leur état, comme un identificateur unique comme un nom ou un ID, ou peut-être une quantité pour une liste.

En outre, comme l'a dit Robbie, remplacer ToString vous permet d'appeler ToString sur une référence aussi élémentaire que le type object.

1
Dave Cousineau

La méthode Object.ToString doit être utilisée uniquement à des fins de débogage. L'implémentation par défaut affiche le nom du type d'objet, ce qui n'est pas très utile. Envisagez de remplacer cette méthode pour fournir de meilleures informations pour les diagnostics et le débogage. Notez que les infrastructures de journalisation utilisent souvent la méthode ToString également. Vous trouverez donc ces fragments de texte dans vos fichiers journaux.

Ne pas renvoyer des ressources de texte localisées dans la méthode Object.ToString. La raison en est que la méthode ToString doit toujours renvoyer quelque chose que le développeur peut comprendre. Le développeur ne parle peut-être pas toutes les langues prises en charge par l'application.

Implémente l'interface IFormattable lorsque vous souhaitez renvoyer un texte localisé convivial. Cette interface définit une surcharge ToString avec les paramètres format et formatProvider. FormatProvider vous aide à formater le texte en tenant compte de la culture.

Voir aussi: Object.ToString et IFormattable

1
jbe

Vous pouvez l'utiliser quand vous avez un objet avec une signification non intuitive de la représentation d'une chaîne, comme personne. Donc, si vous avez besoin, par exemple, d'imprimer cette personne, vous pouvez utiliser ce remplacement pour préparer son format.

1
NickF

Je suis satisfait des directives-cadres mentionnées dans d'autres réponses. Cependant, j'aimerais mettre l'accent sur les objectifs d'affichage et de débogage.

Faites attention à la façon dont vous utilisez ToString dans votre code. Votre code ne doit pas dépendre de la représentation sous forme de chaîne d'un objet. Si tel est le cas, vous devez absolument fournir les méthodes respectives Parse.

Étant donné que ToString peut être utilisé partout, il peut être difficile de maintenir la possibilité de maintenance si vous souhaitez modifier la représentation sous forme de chaîne d'un objet ultérieurement. Dans ce cas, vous ne pouvez pas simplement examiner la hiérarchie des appels pour déterminer si un code risque d’être endommagé.

0
tm1

Plus simple dépend de la façon dont votre propriété va être utilisée. Si vous avez juste besoin de formater la chaîne une fois, il n’est pas logique de la remplacer.

Cependant, il semble que vous substituez la méthode ToString pour ne pas renvoyer les données de chaîne normales de votre propriété, mais pour effectuer un modèle de mise en forme standard. Depuis que vous utilisez string.format avec padding.

Parce que vous avez dit que vous appreniez, l’exercice semble également s’appuyer sur les principes fondamentaux de la programmation orientée objet relative à l’encapsulation et à la réutilisation de code.

String.format, prenant les arguments que vous avez définis pour le remplissage, garantit que la propriété sera formatée de la même manière à chaque fois pour le code qui l'appelle. De plus, pour aller de l'avant, il suffit de le changer à un endroit plutôt qu'à plusieurs.

Bonne question et aussi d'excellentes réponses!

0
SoftwareCarpenter

Je trouve qu'il est utile de remplacer la méthode ToString sur les classes d'entités, car cela permet d'identifier rapidement les problèmes de test, notamment lorsqu'une assertion échoue.

Mais en accord avec ce qui a été dit auparavant, c'est pour donner une représentation lisible par l'homme de l'objet en question.

0
ranjez