なぜ/いつToStringをオーバーライドするのが適切でしょうか?


103

私はC#を勉強していますToStringが、以下の例に示すように、オーバーライドのポイントと利点は何でしょうか。

これは、オーバーライドなしの一般的なメソッドを使用して、もっと簡単な方法で実行できますか?

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

4
実際にそれを使用してUIのどこにでもオブジェクトを表示するかどうかによって異なります。それ以外の場合は、通常はデバッグのためだけです。VSの監視ウィンドウにToString出力が表示されます。(ただし、クラスの属性を使用してこれを実現することもできます。)見出しと出力があるとすると、このプログラムはそれを使用して、Console.WriteLine(obj.GetToStringItemsHeadings); Console.WriteLine(obj);またはを使用してオブジェクトをダンプしているように見えます。
Rup

2
質問がよくわかりません。別の方法でできますか?はい。しかし、なぜそれを別の方法で行いたいのですか?
Konrad Rudolph

5
私は実際にスキップするでしょうGetToString、あなたは例えば取得したい...プロパティ名にConsole.WriteLine(obj.ItemHeadings)
Svish

「これ」を必要としないため、ItemHeadingsプロパティを静的に変更します。
eFloh

:あなたはこの質問をチェックアウトすることをお勧めしますstackoverflow.com/questions/7906828/oo-design-advice-tostring
ユーリGhensev

回答:


127
  • オーバーライドする必要がありますToStringか?番号。

  • 別の方法でオブジェクトの文字列表現を取得できますか?はい。

しかし、使用ToStringすることで、すべてのオブジェクトに共通のメソッドを使用しているため、他のクラスはこのメソッドについて知っています。たとえば、.NETフレームワークがオブジェクトを文字列表現に変換する必要がある場合はいつでも、ToStringそれが最有力候補です(より複雑な書式設定オプションを提供したい場合は他にもあります)。

具体的には、

Console.WriteLine(yourObject);

を呼び出しyourObject.ToString()ます。


13
はい。また、これはMVCやASP.Netの開発に非常に有用である@yourObject<%=yourObject%>「ToStringメソッド-ED」であることを行っています。
Ben Lesh

4
また、コンボボックスに入力する場合、ToStringがデフォルトでアイテムのテキストを取得します
Davi Fiamenghi 2012

3
そのような習慣に注意してください。実話:学生は、文字列を返す以外にメソッドを使用してToStringをオーバーライドし、データを変更しました。彼はプログラムをデバッグしていましたが、プログラムがブレークポイントに立っている間、値は変化し続けました。どうやら-彼が値をチェックするたびにToStringが実行された-そのため、プログラムが実行されていなくても値が変化した。
JNF 2012年

3
@JNF「そのような実践」とは何ですか?ToStringオブジェクトの状態を実際に変更するべきではありません。しかし、これは私が主張したものではありません。
コンラートルドルフ

8
@JNF私はそれを受け入れません。ToStringされるものでオーバーライドします。限目。このステートメントに注意を払うことは、生産的ではありません。あなたがそれを間違えれば–あなた自身の責任です。実際、あなたの生徒ToStringは、David Andersonの回答に投稿されたオーバーライドの公式リストのポイント4に違反しています。
コンラートルドルフ

129

.NET開発シリーズのフレームワーク設計ガイドラインから直接回答をお届けします。

からの例外のスローを避けるToString

インスタンスに関連付けられた一意の文字列を返すCONSIDER

の出力を持つCONSIDERは、ToStringこのタイプの解析メソッドの有効な入力になります。

目に見えるToString副作用がないことを確認してください。

DOのレポートのオーバーライドを使用してセキュリティに敏感な情報ToStringの適切なアクセス権を要求した後にのみ。許可要求が失敗した場合は、セキュリティ上重要な情報を除いた文字列を返します。

このObject.ToStringメソッドは、一般的な表示とデバッグの目的で使用することを目的としています。デフォルトの実装では、オブジェクトタイプ名が提供されるだけです。デフォルトの実装はあまり役に立ちません。メソッドをオーバーライドすることをお勧めします。

DOのオーバーライドToString面白い人間が読める形式の文字列を返すことができますいつでも。デフォルトの実装はあまり有用ではなく、カスタム実装はほとんど常により多くの価値を提供できます。

DOはユニークではなく読めるIDの上にフレンドリ名を好みます。

また、Chris Sold ToStringはユーザーインターフェイスにとって危険なことが多いガイドラインでも説明しているため、言及する価値もあります。一般的に、私の経験則では、UIに情報をバインドするために使用されるプロパティを公開し、ToString診断情報を表示するためのオーバーライドを開発者に残します。タイプで飾ることもできますDebuggerDisplayAttribute

DOがから返された文字列維持しようとToString短いが。デバッガーはToString、開発者に表示されるオブジェクトのテキスト表現を取得するために使用します。文字列がデバッガーが表示できるよりも長い場合、デバッグエクスペリエンスが妨げられます。

DO文化に依存する情報を返すときに、現在のスレッドのカルチャに基づいて書式文字列を。

DOは、過負荷提供ToString(string format)、または実装IFormattableから文字列の戻りがあれば、ToString文化に敏感であるか、文字列をフォーマットするための様々な方法があります。たとえばDateTime、オーバーロードを提供し、を実装しIFormattableます。

しないでくださいから空の文字列またはnullを返しますToString

私はこれらのガイドラインを誓います。あなたはそうすべきです。この1つのガイドラインだけでは、コードがどのように改善されたのかはわかりませんToString。同じことがIEquatable(Of T)やなどにも当てはまりIComparable(Of T)ます。これらにより、コードが非常に機能的になり、時間をかけてコードを実装することを後悔することはありません。

個人的には、ユーザーインターフェイスにToString それほど多くのことを使用したことがなく、ある種のプロパティまたはメソッドを常に公開しています。ほとんどの場合ToString、デバッグおよび開発目的で使用する必要があります。重要な診断情報を表示するために使用します。


6
いい答えです、これらはmsdn.microsoft.com/en-us/library/ms229042.aspxを参照するガイドラインですか?
Jodrell 2012


6
より良い引用が必要です。
Ben Voigt

13
SOがウィキペディアに変わるのは知りませんでした。
デビッドアンダーソン

14
そうではありませんが、ガイドラインを引用すると、ソースを提供することは間違いなくプラスです。
Falanwe 2012

39

オーバーライドToString()することで、クラスの人間が読める文字列表現を提供できます。

これは、出力がクラスに関する有用な情報を明らかにできることを意味します。たとえば、Personクラスがある場合、ToString()出力にその人のID、姓、名などを選択することができます。これは、デバッグやロギングの際に非常に役立ちます。

あなたの例に関して-このクラスが何であるかを知らずにオーバーライドが役立つかどうかを判断することは困難です-しかし、実装自体は問題ありません。


13

それは常に適切ですが、表示しているものの背後にある意図を慎重に検討してください

より良い質問は、尋ねることです:

なぜToString()をオーバーライドするのですか?

ToString()は、オブジェクトの状態へのウィンドウです。要件としての状態の強調。Java / C#のような強力なOOP言語は、すべてをクラスにカプセル化することによってOOPモデルを乱用します。強力なOOPモデルに従っていない言語でコーディングしているとしましょう。クラスと関数のどちらを使用するかを検討します。関数(動詞、アクションなど)として使用し、内部状態が入出力間で一時的にしか維持されない場合、ToString()は値を追加しません。

他の人が述べたように、デバッガや他のシステムで使用される可能性があるため、ToString()でを出力するを検討することが重要です。

ToStringメソッドをオブジェクトの--helpパラメータとして想像したいと思います。短く、読みやすく、わかりやすく、表示しやすいものにする必要があります。これは、オブジェクトがどのように表示すべきである、それは何をしません。これらすべてを考慮して、考えてみましょう...

使用例-TCPパケットの解析:

アプリケーションレベルのみのネットワークキャプチャではなく、pcapキャプチャのようなより重要なもの。

データをコンソールに出力できるように、TCP層だけにToString()をオーバーロードしたいとします。それには何が含まれますか?クレイジーになって、すべてのTCP詳細を解析できます(つまり、TCPは複雑です)...

以下が含まれます:

  • 送信元ポート
  • 宛先ポート
  • シーケンス番号
  • 確認番号
  • データオフセット
  • ウィンドウオフセット
  • チェックサム
  • 緊急ポインタ
  • オプション(私はしませんよでもそこに行くつもり)

しかし、100パケットでTCP.ToString()を呼び出している場合、すべての迷惑メールを受け取りたいですか?もちろん、それは情報過多になるでしょう。簡単で明白な選択も最も賢明です...

人々が見ることを期待するものを明らかにする:

  • 送信元ポート
  • 宛先ポート

私は人間が解析しやすいがYMMVである実用的な出力を好みます。

TCP:[destination:000, source:000]

複雑なことは何もありません。出力はマシンが解析するためのものではありません(つまり、人々がコードを悪用しない限り)。意図された目的は人間が読みやすくすることです。

しかし、私が以前に話したそのジューシーな情報の残りのすべてについてはどうですか、それも役に立ちませんか?私はそれに行きますが、最初に...


ToString()は、これまでで最も価値があり、十分に活用されていないメソッドの1つです。

2つの理由があります。

  1. 人々はToString()が何のためにあるのか理解していない
  2. 基本の「オブジェクト」クラスには、同様に重要な別の文字列メソッドがありません。

理由1-ToString()の有用性を乱用しないでください。

多くの人がToString()を使用して、オブジェクトの単純な文字列表現を引き出します。C#のマニュアルには、次のようにも記載されています。

ToStringは、.NET Frameworkの主要なフォーマットメソッドです。表示に適するようにオブジェクトを文字列表現に変換します。

今後の処理はなく、表示。これは、上記のTCPパケットの素敵な文字列表現を取り、正規表現:: cringe ::を使用して送信元ポートをプルするという意味ではありません。

物事を行うための正しい方法は、SourcePortプロパティでToString()を直接呼び出すことです(BTWはushortなので、ToString()は既に使用可能になっているはずです)。

マシン解析のために複雑なオブジェクトの状態をパッケージ化するために、より堅牢なものが必要な場合は、構造化されたシリアル化戦略を使用するほうがよいでしょう。

幸い、そのような戦略は非常に一般的です。

  • ISerializable(C#)
  • ピクルス(Python)
  • JSON(JavaScriptまたはそれを実装する任意の言語)
  • 石鹸
  • 等...

注:herp-derpのためにPHPを使用しているのでない限り、そのための関数があります:: snicker ::

理由2-ToString()では不十分です:

これをコアで実装する言語はまだ見ていませんが、このアプローチのバリエーションを実際に見て、使用しました。

その一部は次のとおりです。

  • ToVerboseString()
  • ToString(verbose = true)

基本的に、TCPパケットの状態の毛むくじゃらの混乱は、人間が読みやすいように説明する必要があります。TCPについて話している「死んだ馬を打ち負かす」ことを避けるために、ToString()とToVerboseString()が十分に活用されていないと思われる#1のケースに「指を向ける」...

使用例-配列:

主に1つの言語を使用している場合は、おそらくその言語のアプローチに慣れているでしょう。私のように異なる言語間を行き来する人々にとって、さまざまなアプローチの数はイライラすることがあります。

つまり、これが私を苛立たせた回数は、すべてのヒンドゥー教の神のすべての指の合計よりも大きいです。

あり、様々な言語が共通使用例ハックをし、いくつかのことを得るそれを右に。ホイールの再発明が必要なものもあれば、浅いダンプをするものもあれば、深いダンプをするものもあり、どれも私が望んでいるように機能しません...

私が求めているのは非常にシンプルなアプローチです:

print(array.ToString());

出力: 'Array [x]'または 'Array [x] [y]'

ここで、xは最初の次元のアイテム数であり、yは2番目の次元のアイテム数、または2番目の次元がギザギザであることを示す何らかの値です(最小/最大範囲は多分?)。

そして:

print(array.ToVerboseString());

きれいなものに感謝しているので、シバン全体をきれいに出力します。

うまくいけば、これは長い間私を怒らせてきたトピックにいくつかの光を当てるでしょう。少なくとも私は、PHPersがこの回答に反対票を投じるように小さなトロールベイトを振りかけました。


「これをコアに実装する言語はまだ見ていません」と書いてありました。Pythonはこれにかなり近いように思えます。たとえば、配列を出力すると、すべてのデータが簡潔に表示されます。これはC#/ Javaで見逃してしまうことの1つですが、厳密さは好きです。また、ToString()を少し超える必要がある場合は、Pythonのリスト内包構文のようなものを用意すると便利です。string.Join()は似ていますが、柔軟性は低いと思います。
Jon Coombs 2013

1
@JCoombs私は反対しません、理解は素晴らしいです。内包表記を介してPythonのデータ構造をトラバースすると、作業がはるかに簡単になりますが、そのアプローチでも、オブジェクトの内部構造に関する深い知識が必要です。外部ライブラリからインポートされたクラスで常に明らかではない何か。ライブラリの作成者がToString()をオーバーライドして人間が読める形式で有用な表現を提供することは良い習慣ですが、オブジェクトの構造を一般的な構造化された人間が読める形式(JSONなど)で出力する一般的なダンプメソッドがあるとよいでしょう。
Evan Plaice 2013

優れた点。そのため、基本的にtypeof以外のものを表示する代わりに、JSONのようなものに自動シリアル化する場合があります。(これまでのところ、XMLにシリアル化された.NETオブジェクトしか見たことがありませんが、.NETは初めてです)しかし、危険なのは、ToString()を機械可読として扱う誘惑かもしれません。(たとえば、それを逆シリアル化できると期待しています。)
Jon Coombs 2013

1
@JCoombsはい、機能レベルではJSONとXMLは同じ目的を果たします。多くの人がToStringを機械可読出力として使用しています。それが悪いかどうかは議論の余地があります。私の最大の牛肉は-多くの場合-人間が読める形式でオブジェクトの状態を出力するのは面倒です。ToString()の実装は十分に活用されていないことが多く、デバッガーウォッチリストの外でオブジェクトの状態を読み取るのは面倒です。シリアル化された表現は、オブジェクト全体の状態を表示するためのバックアップとして適していますが、ToStringのより良い実装が最良のオプションです。
Evan Plaice 2013

ToString()デバッグを目的としている場合、StringBuilderからテキスト全体を抽出する唯一の方法はなぜですか。これは重要な関数ですか。
Dan W

9

本当に、それは何よりも良い実践についてです。

ToString()オブジェクトの文字列表現を返すために多くの場所で使用され、一般的には人間が使用します。多くの場合、同じ文字列を使用してオブジェクトを再水和することができます(intまたはDateTimeなど)、それが常に与えられるとは限りません(たとえば、ツリーには、Countを表示するだけの便利な文字列表現があるかもしれませんが、明らかに使用できませんそれを再構築すること)。

特に、デバッガーはこれを使用してウォッチウィンドウ、イミディエイトウィンドウなどに変数を表示するためToString、実際にはデバッグに非常に役立ちます。

一般に、そのような型には、文字列を返す明示的なメンバーも含まれることがよくあります。たとえば、Lat / LongペアはToDecimalDegreesを返す可能性があります"-10, 50"ToDegreesMinutesSeconds、も返す可能性があります。これはLat / Longペアの別の形式であるためです。その同じタイプはToString、これらのいずれかでオーバーライドして、デバッグなどの「デフォルト」を提供したり、Webページのレンダリングなどの可能性を提供したりすることもできます(たとえば、@Razor のコンストラクトはToString()非文字列式の結果を出力ストリーム)。


6

object.ToString()オブジェクトをその文字列表現に変換します。ToString()作成したクラスをユーザーが呼び出したときに返されるものを変更する場合ToString()は、そのクラスでオーバーライドする必要があります。


6

最も有用な情報は既に提供されていると思いますが、2セント追加します。

  • ToString()上書きされることを意味します。そのデフォルトの実装は型名を返しますが、(特に多くのオブジェクトを処理する場合は)時には役立つかもしれませんが、大部分の場合には十分ではありません。

  • デバッグの目的では、を信頼できることに注意してくださいDebuggerDisplayAttribute。詳細については、こちらをご覧ください

  • 原則として、POCOではいつでもオーバーライドできますToString()。POCOはデータの構造化された表現であり、通常は文字列になります。

  • オブジェクトのテキスト表現になるようにToStringを設計します。多分その主なフィールドとデータ、多分コレクション内のアイテム数の説明など。

  • 常にその文字列を1行に収め、重要な情報のみを入力するようにしてください。PersonName、Address、Numberなどのプロパティを持つクラスがある場合は、メインデータ(Name some ID number)のみを返します。

  • の適切な実装を上書きしないように注意してくださいToString()。一部のフレームワーククラスはすでに実装していますToString()。そのデフォルトの実装をオーバーライドすることは悪いことです。人々は特定の結果を期待し、ToString()別の結果を得るでしょう。

の使用を本当に恐れないでくださいToString()。注意が必要なのは、機密情報を返すことだけです。それ以外は、リスクは最小限です。確かに、一部の人が指摘したように、他のクラスは情報に到達するたびにToStringを使用します。しかし、一体、型名を返すことは、実際の情報を取得するよりも優れていると考えられますか?


5

オーバーライドしないとToString、基本クラスの実装が取得されますObject。これは、クラスの短い型名です。

その他の、より意味のある、または有用な実装が必要な場合はToString、オーバーライドしてください。


データソースとしてあなたのタイプのリストを使用する場合に役立ちますListBoxようにToString自動的に表示されます。

別の状況は、型の表現を取得するためString.Formatに呼び出すToStringに型を渡したい場合に発生します。


4

まだ誰も言及していないこと:をオーバーライドすることで、次のことができるようにToString()実装を検討することもIFormattableできます。

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

どちらが便利か。


オーバーライドするメソッドを提供するフレームワーククラスの例を教えてください。フレームワークで私が知っている唯一の関連するものはIFormattibleインターフェースです。
tm1

@ tm1継承元のオブジェクトにSystem.Objectはオーバーライド可能なToString()メソッドがありますが、フォーマッタメソッドはそうであっoverrideてはならずIFormattible、完全に準拠するために実際にインターフェイスを参照する必要があります。
Zhaph-Ben Duguid

ほとんど- パラメータをIFormattable取りIFormatProviderます。投稿を編集していただきありがとうございます。
tm1

実際、これには2つの方法があり、私が表現していた値は示されているものでした;)
Zhaph-Ben Duguid


2

場合によっては、デバッガーウォッチウィンドウでカスタムクラスの値を読みやすくなります。ウォッチウィンドウに表示したいものが正確にわかっている場合、その情報でToStringをオーバーライドすると、それが表示されます。


1

構造体(実質的にユーザープリミティブ)を定義するとき、特にXMLシリアル化のために、マッチングToStringParseおよびTryParseメソッドを使用することをお勧めします。この場合は、状態全体を文字列に変換するため、後で読み取ることができます。

ただし、クラスはより複雑な構造であり、通常ToString、およびを使用するには複雑すぎParseます。それらのToStringメソッドは、状態全体を保存するのではなく、名前やIDなどの一意の識別子や、リストの数量など、状態を識別するのに役立つ簡単な説明にすることができます。

また、ロビーが言ったように、オーバーライドToStringすることでToString、typeと同じくらい基本的な参照を呼び出すことができますobject


1

personのように、文字列表現の直感的な意味を持たないオブジェクトがある場合に使用できます。したがって、たとえばこの人物を印刷する必要がある場合は、このオーバーライドを使用してフォーマットを準備できます。


1

このObject.ToStringメソッドは、デバッグ目的でのみ使用してください。デフォルトの実装では、あまり役に立たないオブジェクトタイプ名が表示されます。診断とデバッグのためにより良い情報を提供するために、このメソッドをオーバーライドすることを検討してください。ロギングインフラストラクチャでもToStringメソッドがよく使用されるため、これらのテキストフラグメントがログファイルで見つかることを考慮してください。

Object.ToStringメソッド内でローカライズされたテキストリソースを返さないでください。その理由は、ToStringメソッドは常に、開発者が理解できるものを返す必要があるためです。開発者は、アプリケーションがサポートするすべての言語を話すとは限りません。

IFormattableユーザーフレンドリーなローカライズされたテキストを返す場合は、インターフェースを実装します。このインターフェイスは、パラメーターformatとを使用してToStringオーバーロードを定義しますformatProvider。formatProviderは、文化に配慮した方法でテキストをフォーマットするのに役立ちます。

参照:Object.ToStringおよびIFormattable


0

よりシンプルなのは、プロパティがどのように使用されるかによって異なります。文字列を一度だけフォーマットする必要がある場合、それをオーバーライドすることはあまり意味がありません。

ただし、ToStringメソッドをオーバーライドして、プロパティの通常の文字列データを返さずに、標準のフォーマットパターンを実行しているようです。パディング付きのstring.formatを使用しているため。

学習しているとおっしゃっていたので、この演習は、カプセル化とコードの再利用に関連するオブジェクト指向プログラミングのコア原則にも当てはまるようです。

パディング用に設定した引数を取るstring.formatは、プロパティを呼び出すコードに対して毎回同じ方法でプロパティがフォーマットされることを保証します。同様に、今後は、多くの場所ではなく1つの場所で変更するだけで済みます。

素晴らしい質問といくつかの素晴らしい答え!


0

特にアサーションが失敗したときにテストコンソールがオブジェクトのToStringメソッドを呼び出すと、テストの問題をすばやく特定できるので、エンティティクラスのToStringメソッドをオーバーライドすると便利です。

しかし、それが問題のオブジェクトの人間が読める表現を与えることである前に言われていたことに同意します。


0

他の回答で参照されているフレームワークガイドラインに満足しています。ただし、表示とデバッグの目的を強調したいと思います。

ToStringコードでの使用方法に注意してください。コードは、オブジェクトの文字列表現に依存するべきではありません。もしそうなら、あなたは絶対にそれぞれのParse方法を提供するべきです。

ToStringどこでも使用できるので、後でオブジェクトの文字列表現を変更したい場合は、保守性の問題になる可能性があります。この場合、呼び出し階層を調べて、コードが壊れるかどうかを調べることはできません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.