为什么/何时覆盖ToString是否合适?

时间:2012-04-23 09:36:25

标签: c# override

我正在学习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;
}

17 个答案:

答案 0 :(得分:128)

我将直接从.NET开发系列的Framework Design Guidelines给出答案。

避免ToString

中抛出异常

CONSIDER 返回与该实例关联的唯一字符串。

输出ToString

CONSIDER 是此类型上任何解析方法的有效输入。

DO 确保ToString没有可观察到的副作用。

DO 仅在要求适当的权限后才通过覆盖ToString来报告安全敏感信息。如果权限请求失败,则返回不包含安全敏感信息的字符串。

Object.ToString方法旨在用于一般显示和调试目的。默认实现只提供对象类型名称。默认实现不是很有用,建议重写该方法。

只要可以返回有趣的人类可读字符串,

DO 就会覆盖ToString。默认实现不是很有用,自定义实现几乎总能提供更多价值。

DO 更喜欢友好名称,而不是唯一但不可读的ID。

值得一提的是,Chris Sells也在指南中解释ToString通常对用户界面很危险。通常,我的经验法则是公开一个属性,该属性将用于将信息绑定到UI,并保留ToString覆盖以向开发人员显示诊断信息。您也可以使用DebuggerDisplayAttribute修饰您的类型。

DO 尝试保持从ToString返回的字符串短。调试器使用ToString来获取要向开发人员显示的对象的文本表示。如果字符串比调试器可以显示的长,则调试体验受阻。

返回与文化相关的信息时,

DO 基于当前线程文化的字符串格式。

DO 提供重载ToString(string format),或实现IFormattable,如果从ToString返回的字符串是区域性敏感的,或者有多种方法来格式化字符串。例如,DateTime提供重载并实现IFormattable

请勿ToString

返回空字符串或空值

我发誓这些指导方针,你应该这样做。我不能告诉你我的代码是如何通过这个ToString指南改进的。 IEquatable(Of T)IComparable(Of T)之类的内容也是如此。这些东西使你的代码非常实用,你不会后悔花费额外的时间来实现它们。

就个人而言,我从来没有真正使用ToString 用于用户界面,我总是暴露某种属性或方法。大多数情况下,您应该使用ToString进行调试和开发。用它来显示重要的诊断信息。

答案 1 :(得分:127)

  • 您需要覆盖ToString吗?

  • 您能以另一种方式获得对象的字符串表示吗?

但是通过使用ToString,您使用的是所有对象所共有的方法,因此其他类知道此方法。例如,每当.NET框架想要将对象转换为字符串表示时,ToString是主要候选者(如果您想提供更精细的格式化选项,还有其他人)。

具体而言,

Console.WriteLine(yourObject);

会调用yourObject.ToString()

答案 2 :(得分:39)

覆盖ToString()允许您为类提供有用的人类可读字符串表示。

这意味着输出可以显示有关您班级的有用信息。例如,如果你有一个Person类,你可以选择让ToString()输出该人的id,他们的名字,姓氏等。这在调试或记录时非常有用。

关于你的例子 - 很难判断你的覆盖是否有用而不知道这个类是什么 - 但是实现本身是可以的。

答案 3 :(得分:13)

它始终是合适的,但请仔细考虑您所展示的内容背后的意图

更好的问题是问:

  

为什么会覆盖ToString()?

ToString()是进入对象状态的窗口。强调为要求。强大的OOP语言(如Java / C#)通过将所有内容封装在一个类中来滥用OOP模型。想象一下,您使用的语言并不遵循强大的OOP模型;考虑您是否使用类或函数。如果你将它用作函数(即动词,动作)并且内部状态仅在输入/输出之间暂时保持,则ToString()不会增加值。

与其他人提到的一样,考虑使用ToString()输出的内容非常重要,因为它可以被调试器或其他系统使用。

我喜欢将ToString方法想象为对象的--help参数。它应该简短,可读,明显且易于显示。它应该显示对象 的内容而不是所做的。考虑到所有这些,请考虑......

用例 - 解析TCP数据包:

不仅仅是应用程序级别的网络捕获,而是像pcap捕获更多的肉类。

您希望仅为TCP层重载ToString(),以便可以将数据打印到控制台。它包括什么?您可能会发疯并解析所有TCP细节(即TCP很复杂)......

其中包括:

  • 源端口
  • 目的港
  • 序号
  • 确认编号
  • 数据偏移
  • 标志
  • 窗口偏移
  • 校验
  • 紧急指针
  • 选项(我甚至不会去那里)

但是如果你在100个数据包上调用TCP.ToString(),你想要收到所有垃圾吗?当然不是,这将是信息超载。简单而明显的选择也是最明智的......

揭露人们期望看到的内容:

  • 源端口
  • 目的港

我更喜欢合理的输出,人类可以轻松解析YMMV

TCP:[destination:000, source:000]

没有什么复杂的,输出不是机器解析的(即除非人们滥用你的代码),预期目的是为了人类的可读性。

但是之前我谈过的那些多余的信息呢,那也不是很有用吗?我会先做到这一点,但首先......


ToString()有史以来最有价值和最少使用的方法之一

有两个原因:

  1. 人们不了解ToString()的用途
  2. 基地'对象' class缺少另一个同样重要的字符串方法。
  3. 原因1 - 不要滥用ToString()的用处:

    很多人使用ToString()来拉取对象的简单字符串表示。 C#手册甚至声明:

      

    ToString是.NET Framework中的主要格式化方法。它将对象转换为字符串表示形式,以便适合显示。

    显示,无法进一步处理。这并不意味着,使用我上面的TCP数据包的漂亮字符串表示并使用regex :: cringe ::拉取源端口。

    正确做事的方法是直接在SourcePort属性上调用ToString()(BTW是一个ushort,所以ToString()应该已经可用了。)

    如果您需要更强大的功能来打包复杂对象的状态以进行机器解析,那么您最好使用结构化序列化策略。

    幸运的是,这种策略很常见:

    • ISerializable(C#)
    • Pickle(Python)
    • JSON(Javascript或任何实现它的语言)
    • SOAP
    • 等...

    注意:除非您使用PHP,因为herp-derp,那里有一个函数:: snicker ::

    原因2 - ToString()还不够:

    我还没有看到一种在核心实现此功能的语言,但我已经看到并在野外使用了这种方法的变体。

    其中一些包括:

    • ToVerboseString()
    • 的ToString(详细=真)

    基本上,为了人类的可读性,应该描述TCP数据包的状态的多毛混乱。为了避免殴打死马'谈论TCP我会指出一个手指'在#1的情况下,我认为ToString()和ToVerboseString()未被充分利用......

    用例 - 数组:

    如果您主要使用一种语言,那么您可能会对该语言的方法感到满意。对于像我这样在不同语言之间跳跃的人来说,各种方法的数量可能会令人恼火。

    也就是说,这让我感到烦恼的次数大于每个印度教神的所有手指的总和。

    various种语言使用常见的hacksfew get right。有些需要轮子重新发明,有些需要进行浅层转储,有些需要进行深度转储,其中没有一种按我喜欢的方式工作......

    我要求的是一种非常简单的方法:

    print(array.ToString());
    
      

    输出:'数组[x]'或者'数组[x] [y]'

    其中x是第一维中的项数,y是第二维中的项数或某个值,表示第二维是锯齿状的(可能是最小/最大范围?)。

    print(array.ToVerboseString());
    
      

    因为我很欣赏漂亮的东西,所以整个产生了漂亮的印刷品。

    希望这能够解释一个长期困扰我的话题。至少我为PHP人员撒了一个小小的诱饵,以便回答这个问题。

答案 4 :(得分:9)

这真的是关于良好的做法,真的。

在许多地方使用

ToString()来返回对象的字符串表示,通常供人类使用。通常可以使用相同的字符串来重新水化对象(例如,考虑intDateTime),但这并不总是给定的(例如树,可能有一个有用的字符串表示,只显示一个伯爵,但显然你不能用它来重建它。)

特别是调试器将使用它来在监视窗口,即时窗口等中显示变量,因此ToString对于调试来说实际上非常有价值。

通常,这样的类型通常也会有一个显式成员返回一个字符串。例如,Lat / Long对可能有ToDecimalDegrees,例如返回"-10, 50",但它也可能有ToDegreesMinutesSeconds,因为这是Lat / Long对的另一种格式。然后,同一类型也可以使用其中一个覆盖ToString来为调试之类的内容提供“默认”,甚至可能用于渲染网页等内容(例如,Razor中的@构造写入输出流的非字符串表达式的ToString()结果。)

答案 5 :(得分:6)

object.ToString()将对象转换为其字符串表示形式。如果您想要更改用户在您创建的类上调用ToString()时返回的内容,则需要覆盖该类中的ToString()

答案 6 :(得分:6)

虽然我认为已经提供了最有用的信息,但我将加上我的两分钱:

  • ToString()意味着被覆盖。它的默认实现返回类型名称,虽然有时可能很有用(特别是在处理大量对象时),但在绝大多数情况下都不够。

  • 请记住,出于调试目的,您可以依赖DebuggerDisplayAttribute。您可以阅读更多相关信息here

  • 通常,在POCO上,您始终可以覆盖ToString()。 POCO是数据的结构化表示,通常可以成为字符串。

  • 将ToString设计为对象的文本表示。也许它的主要领域和数据,可能是集合中有多少项目的描述等。

  • 始终尝试将该字符串放入一行并且只包含基本信息。如果您具有名称,地址,编号等属性的Person类,则仅返回主数据(名称为某个ID号)。

  • 小心不要覆盖ToString()的良好实现。一些框架类已经实现了ToString()。覆盖默认实现是一件坏事:人们会期望来自ToString()的某个结果并获得另一个结果。

不要害怕使用ToString()。我唯一要注意的是返回敏感信息。除此之外,风险很小。当然,正如一些人所指出的那样,其他类在获取信息时会使用你的ToString。但是,何时返回类型名称会被认为比获取实际信息要好?

答案 7 :(得分:5)

如果你没有覆盖ToString,那么就会得到你的基类实现,因为Object只是类的短类型名称。

如果您想要ToString的其他一些更有意义或更有用的实现,请覆盖它。


当您使用类型列表作为ListBox的数据源时,此功能非常有用,因为ToString会自动显示。

如果您希望将类型传递给String.Format,并调用ToString来获取您的类型的代表,则会出现另一种情况。

答案 8 :(得分: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;
}

哪个有用。

答案 9 :(得分:3)

重写ToString()的一个好处是Resharper的工具支持:Alt + Ins - > “格式化成员”并为您编写ToString()。

答案 10 :(得分:2)

在某些情况下,它可以更容易地在调试器监视窗口中读取自定义类的值。如果我确切地知道我想在观察窗口中看到什么,当我用该信息覆盖ToString时,我就会看到它。

答案 11 :(得分:1)

当你的对象具有字符串表示的直观含义时,你可以使用它,比如person。因此,如果您需要打印此人,可以使用此覆盖来准备其格式。

答案 12 :(得分:1)

在定义结构(有效的用户原语)时,我发现使用匹配的ToStringParseTryParse方法是一种很好的做法,特别是对于XML序列化。在这种情况下,您将整个状态转换为字符串,以便以后可以读取它。

然而,

类是更复杂的结构,通常过于复杂,无法使用ToStringParse。他们的ToString方法,而不是保存整个状态,可以是一个简单的描述,可以帮助您识别他们的状态,如名称或ID的唯一标识符,或列表的数量。

此外,正如Robbie所说,覆盖ToString允许您在ToString类型的基础上调用object

答案 13 :(得分:1)

Object.ToString方法仅用于调试目的。默认实现显示对象类型名称,它不是很有用。请考虑重写此方法,以便为诊断和调试提供更好的信息。请注意,日志记录基础结构通常也使用ToString方法,因此您可以在日志文件中找到这些文本片段。

Object.ToString方法中返回本地化文本资源。原因是ToString方法应该总是返回开发人员可以理解的东西。开发人员可能不会说应用程序支持的所有语言。

当您想要返回用户友好的本地化文本时,

实施 IFormattable界面。此接口使用参数formatformatProvider定义ToString重载。 formatProvider可帮助您以文化感知方式格式化文本。

另请参阅:Object.ToString and IFormattable

答案 14 :(得分:0)

更简单取决于您的财产将如何使用。如果你只需要将字符串格式化一次,那么覆盖它就没有多大意义。

但是,您似乎重写了ToString方法,不返回属性的正常字符串数据,而是执行标准格式设置模式。因为你使用带有填充的string.format。

因为你说你正在学习,所以练习似乎也涉及与封装和代码重用有关的面向对象编程的核心原则。

使用您为填充设置的参数的string.format确保每次调用它的任何代码都将以相同的方式格式化该属性。同样,前进你只需要在一个地方而不是很多地方改变它。

很棒的问题,还有一些很棒的答案!

答案 15 :(得分:0)

我发现覆盖实体类的ToString方法很有用,因为它有助于快速识别测试中的问题,尤其是当断言失败时,测试控制台将调用对象上的ToString方法。

但是,与之前所说的一致是为了给出有关对象的人类可读表示。

答案 16 :(得分:0)

我对其他答案中提到的框架准则感到满意。 但是,我想强调显示和调试的目的。

请注意如何在代码中使用ToString。您的代码不应依赖于对象的字符串表示形式。如果是这样,则应绝对提供相应的Parse方法。

由于ToString可以在任何地方使用,因此如果您想在以后的某个时间更改对象的字符串表示形式,可能会成为可维护性的痛点。在这种情况下,您不能仅检查调用层次结构来研究某些代码是否会中断。