C#: ToString() 应该如何实现?

7
问题如下:
  • GUI库通常使用ToString作为类的默认表示。因此,需要本地化。
  • ToString用于记录日志。它应该提供程序相关信息,不应进行翻译,并包括代理键和枚举值等内部状态。
  • ToString被许多接受对象作为参数的字符串操作使用,例如向流写入时的String.Format。根据上下文,您可能会期望不同的结果。
  • 如果有许多不同形式的相同对象(例如,长表单和短表单),则ToString过于有限。

由于使用方式不同,有许多不同类型的实现。因此,它们太不可靠,不能真正有用。

如何实现ToString才能有效?在什么情况下应该使用ToString,在什么情况下应该避免使用?


.NET Framework 文档中提到:

该方法返回一个与文化相关的可读字符串。

有一个类似的问题,但并不完全相同。

5个回答

5

看起来你对一个微小的方法有很大的期望 :) 据我所知,在许多不同的情况下使用通用方法并不是一个好主意,特别是当它的行为可能因类而异。

以下是我的建议:

1.不要让GUI库使用你对象的ToString()方法。相反,使用更有意义的属性(几乎所有控件都可以自定义显示其他属性而不是ToString),例如使用DisplayMember。 2.在获取有关对象的某些信息(用于日志记录或其他用途)时,让某个人(另一个对象或对象本身)决定提供什么以及如何显示它。(策略模式可能会有所帮助)


如果它没有在许多地方被隐式调用,我不会期望那么多。但你可能是对的。我应该避免它在界面中被隐式使用。 - Stefan Steinegger

2

这是一篇很好的文章,解释了如何重写System.Object.ToString()并实现IFormattable。你可以点击这里查看。


1
链接损坏,出现503错误! - Kyle Baran

1

这取决于您的类的预期用途。许多类没有自然的字符串表示(例如Form对象)。在这种情况下,我会将ToString实现为一个信息性方法(Form文本、大小等),有助于调试。

如果该类旨在向用户提供信息,则我会将ToString实现为该值的默认表示。例如,如果您有一个Vector对象,则ToString可能返回向量作为X和Y坐标。在这里,如果有其他描述类的方法,我也会添加替代方法。因此,对于Vector,我可能会添加一个方法,返回一个角度和长度的描述。

出于调试目的,您还可以考虑向您的类添加DebuggerDisplay属性。这告诉调试器如何显示类,但它不影响字符串表示。

您还可以考虑使ToString返回的值可解析,以便可以从字符串表示创建对象。就像使用Int32.Parse方法一样。


1
考虑到ToString方法与Visual Studio的调试器之间的紧密集成,需要注意另一个细节。 Watch窗口将ToString的结果显示为表达式的值,因此,如果您的方法执行任何惰性加载、具有任何副作用或需要很长时间,则可能会看到奇怪的行为或调试器似乎停顿。尽管这些特性不是良好设计的ToString方法的标志,但它们确实存在(例如,天真的“从数据库获取翻译”的实现)。
因此,我认为默认的ToString方法(没有参数)是Visual Studio调试钩子——这意味着它通常不应被重载以供程序在调试环境之外使用。
虽然熟悉该领域的人使用调试属性(DebuggerTypeProxyAttribute,DebuggerDisplayAttribute,DebuggerBrowsableAttribute)来自定义调试器,但许多人(包括我自己)通常认为由ToString生成的默认输出并显示在Watch窗口中已经足够好了。
我理解这是一种相当严格的观点——把ToString视为调试钩子——但我发现实现IFormattable似乎是更可靠和扩展的路线。

0
个人而言,我不常实现 ToString 方法。在许多情况下,这样做并没有多大意义,因为一个类型的主要作用可能是定义行为,而不是数据。在其他情况下,它根本不重要,因为没有客户端需要它。
无论如何,以下是一些有意义的情况(不是详尽无遗的列表):
  • 如果 ToString 的结果可以被解析回一个没有数据丢失的类型实例。
  • 当类型具有简单(即非复杂)值时。
  • 当类型的主要目的是将数据格式化为文本时。
我不认为你列出的使用场景之间存在冲突。当显示是主要目的时,ToString 应该提供用户友好的文本,但对于日志记录(或者,正如你所描述的,用于跟踪),我会说您不应该在任何情况下跟踪 UI 特定元素,而是跟踪其目的是编写详细跟踪数据的对象。
因此,没有冲突,因为根据单一职责原则,它不应该是相同的类型。
请记住,如果需要更多控制,您始终可以重载 ToString 方法。

“从未有客户需要它”的问题在于,ToString被隐式调用。当同一对象在不同情况下使用时就会产生冲突。DateTime是UI特定的类吗?许多类不仅设计用于UI或仅内部使用。“您总是可以重载ToString”:这就是问题所在:应该如何实现? - Stefan Steinegger
如果您在非常不同的情况下使用相同的对象,我认为您正在违反SRP原则... 不,DateTime并不是一个特别针对UI的类,但它属于可以进行ToString/Parse往返操作而不会丢失数据的类别。 - Mark Seemann

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接