简体   繁体   English

为什么object.ToString()存在?

[英]Why does object.ToString() exist?

Isn't it much more elegant and neat to have an IStringable interface? 拥有一个IStringable接口不是更优雅和整洁吗?

Who needs this Type.FullName object returned to us? 谁需要这个Type.FullName对象返回给我们?

EDIT: everyone keeps asking why do I think it's more elegant.. 编辑:每个人都在问为什么我觉得它更优雅..

Well, it's just like that, instead of IComparable, object would have CompareTo method, that by default throws an exception or returns 0. 好吧,它就是这样,而不是IComparable,对象会有CompareTo方法,默认情况下抛出异常或返回0。

There are objects that cannot and should not be described as a string. 有些对象不能也不应该被描述为字符串。 object could have equally returned string.Empty . 对象可以同样返回string.Empty Type.FullName is just an arbitrary choice.. Type.FullName只是一个任意选择..

And for methods such as Console.Write(object), I think it should be: Write(IStringable). 对于Console.Write(对象)之类的方法,我认为应该是:Write(IStringable)。

However, if you are using WriteLine to anything but strings (or something that its ToString is obvious such as numbers), it seems to me it's for debugging mode only.. 但是,如果你将WriteLine用于除字符串之外的任何东西(或其ToString很明显的东西,如数字),在我看来它只适用于调试模式..

By the way - how should I comment to you all? 顺便说一句 - 我该怎么评论你们所有人? Is it okay that I post an answer? 我发布答案可以吗?

There are three virtual methods that IMHO should have never been added to System.Object... 恕我直言应该有三种虚拟方法从未添加到System.Object ...

  • ToString() 的ToString()
  • GetHashCode() GetHashCode的()
  • Equals() 等于()

All of these could have been implemented as you suggest with an interface. 所有这些都可以按照您建议的界面实现。 Had they done so I think we'd be much better off. 如果他们这样做了我认为我们会好得多。 So why are these a problem? 那么为什么这些问题呢? Let's just focus on ToString(): 我们只关注ToString():

  1. If ToString() is expected to be implemented by someone using ToString() and displaying the results you have an implicit contract that the compiler cannot enforce. 如果ToString()应该由使用ToString()的人实现并显示结果,那么您将拥有编译器无法强制执行的隐式契约。 You assume that ToString() is overloaded, but there is no way to force that to be the case. 您假设ToString()被重载,但没有办法强制这样做。
  2. With an IStringable you would only need to add that to your generic type-constraint or derive your interface from it to require it's usage on implementing objects. 使用IStringable,您只需要将其添加到泛型类型约束或从中派生接口,以要求它在实现对象时使用。
  3. If the benefit you find in overloading ToString() is for the debugger, you should start using [System.Diagnostics.DebuggerDisplayAttribute]. 如果您在重载ToString()时发现的好处是针对调试器,则应该开始使用[System.Diagnostics.DebuggerDisplayAttribute]。
  4. As for needing this implementation for converting objects to strings via String.Format(), and/or Console.WriteLine, they could have deferred to the System.Convert.ToString(object) and checked for something like 'IStringable', failing over to the type's name if not implemented. 至于需要通过String.Format()和/或Console.WriteLine将对象转换为字符串的实现,它们可能已经延迟到System.Convert.ToString(对象)并检查了类似'IStringable'的内容,故障转移到如果没有实现,类型的名称。
  5. As Christopher Estep points out, it's culture specific. 正如克里斯托弗埃斯特普指出的那样,它具有特定的文化特征。

So I guess I stand alone here saying I hate System.Object and all of it's virtual methods. 所以我想我独自站在这里说我讨厌System.Object及其所有虚拟方法。 But I do love C# as a whole and overall I think the designers did a great job. 但我确实喜欢C#作为一个整体而且整体而言我认为设计师做得很好。

Note: If you intend to depend upon the behavior of ToString() being overloaded, I would suggest you go ahead and define your IStringable interface. 注意:如果您打算依赖于ToString()的重载行为,我建议您继续定义您的IStringable接口。 Unfortunatly you'll have to pick another name for the method if you really want to require it. 不幸的是,如果你真的想要它,你将不得不为该方法选择另一个名称。

more 更多

My coworkers and I were just speaking on the topic. 我的同事和我刚刚谈到这个话题。 I think another big problem with ToString() is answering the question "what is it used for?". 我认为ToString()的另一个大问题是回答“它用于什么?”的问题。 Is it Display text? 是显示文字吗? Serialization text? 序列化文字? Debugging text? 调试文字? Full type name? 全姓名?

Having Object.ToString makes APIs like Console.WriteLine possible. 让Object.ToString使得像Console.WriteLine这样的API成为可能。

From a design perspective the designers of the BCL felt that the ability to provide a string representation of an instance should be common to all objects. 从设计的角度来看,BCL的设计者认为提供实例的字符串表示的能力应该对所有对象都是通用的。 True full type name is not always helpful but they felt the ability to have customizable representation at a root level outweighed the minor annoyance of seeing a full type name in output. 真正的完整类型名称并不总是有用,但他们觉得在根级别具有可自定义表示的能力超过了在输出中看到完整类型名称的轻微烦恼。

True you could implement Console.WriteLine with no Object.ToString and instead do an interface check and default to the full name of the type if the interface was not present. 如果没有Object.ToString,您可以实现Console.WriteLine,而是执行接口检查,如果接口不存在,则默认为该类型的全名。 But then every single API which wanted to capture the string representation of an object instance would have to implement this logic. 但是,每个想要捕获对象实例的字符串表示的API都必须实现此逻辑。 Given the number of times Object.ToString is used just within the core BCL, this would have lead to a lot of duplication. 考虑到在核心BCL中使用Object.ToString的次数,这将导致大量重复。

I imagine it exists because it's a wildly convenient thing to have on all objects and doesn't require add'l cruft to use. 我想它存在是因为它对所有物体都是非常方便的东西,并且不需要使用add'l cruft。 Why do you think IStringable would be more elegant? 为什么你认为IStringable会更优雅?

Not at all. 一点也不。

It doesn't need to be implemented and it returns culture-specific results. 它不需要实现它返回特定于文化的结果。

This method returns a human-readable string that is culture-sensitive. 此方法返回对文化敏感的人类可读字符串。 For example, for an instance of the Double class whose value is zero, the implementation of Double..::.ToString might return "0.00" or "0,00" depending on the current UI culture. 例如,对于值为零的Double类的实例,Double .. ::。ToString的实现可能返回“0.00”或“0,00”,具体取决于当前的UI文化。

Further, while it comes with its own implementation, it can be overriden, and often is. 此外,虽然它有自己的实现,但它可以被覆盖,而且往往是。

Why make it more complicated? 为什么要把它变得更复杂? The way it is right now basically establishes that each and every object is capable of printing its value to a string, I can't see anything wrong with that. 它现在的方式基本上确定每个对象都能够将其值打印到字符串,我看不出有什么问题。

嗯,所以可以在派生类中重写它?

“可串行”表示在很多场景中都很有用,库设计者可能认为ToString()更直接。

With IStringable, you will have to do an extra check/cast to see if you can output an object in string format. 使用IStringable,您将不得不进行额外的检查/强制转换,以查看是否可以以字符串格式输出对象。 It's too much of a hit on perf for such a common operation that should be a good thing to have for 99.99% of all objects anyway. 对于这样一个常见的操作而言,这对于性能的影响太大了,无论如何对99.99%的所有对象都应该是一件好事。

Structs and Objects both have the ToString() member to ease debugging. Structs和Objects都具有ToString()成员以便于调试。

The easiest example of this can be seen with Console.WriteLine which receives a whole list of types including object, but also receives params object[] args . 最简单的例子可以在Console.WriteLine中看到,它接收包括object在内的整个类型列表,但也接收params object[] args As Console is often a layer on-top of TextWriter these statements are also helpful (sometimes) when writing to files and other streams (sockets). 由于Console通常是TextWriter的一个层,因此在写入文件和其他流(套接字)时,这些语句也很有用(有时)。

It also illustrates a simple object oriented design that shows you interfaces shouldn't be created just because you can. 它还说明了一个简单的面向对象设计,它表明您不应仅仅因为可以创建接口。

My new base class: 我的新基类:

class Object : global::System.Object
{
    [Obsolete("Do not use ToString()", true)]
    public sealed override string ToString()
    {
        return base.ToString();
    }

    [Obsolete("Do not use Equals(object)", true)]
    public sealed override bool Equals(object obj)
    {
        return base.Equals(this, obj);
    }

    [Obsolete("Do not use GetHashCode()", true)]
    public sealed override int GetHashCode()
    {
        return base.GetHashCode();
    }
}

There's indeed little use of having the Type.FullName returned to you, but it would be even less use if an empty string or null were returned. 确实没有将Type.FullName返回给你的Type.FullName ,但是如果返回一个空字符串或null则更少使用它。 You ask why it exists. 你问为什么它存在。 That's not too easy to answer and has been a much debated issue for years. 这不是太容易回答,多年来一直是一个备受争议的问题。 More then a decade ago, several new languages decided that it would be convenient to implicitly cast an object to a string when it was needed, those languages include Perl, PHP and JavaScript, but none of them is following the object orientation paradigm thoroughly. 十多年前,几种新语言决定在需要时隐式地将对象强制转换为字符串会很方便,这些语言包括Perl,PHP和JavaScript,但它们都没有完全遵循面向对象的范例。

Approaches 途径

Designers of object oriented languages had a harder problem. 面向对象语言的设计者有一个更难的问题。 In general, there were three approaches for getting the string representation of an object: 通常,有三种方法可以获取对象的字符串表示形式:

  • Use multiple inheritance, simply inherit from String as well and you can be cast to a string 使用多重继承,只需从String继承,您就可以转换为字符串
  • Single inheritance: add ToString to the base class as a virtual method 单继承:将ToString作为虚方法添加到基类
  • Either: make the cast operator or copy constructor overloadable for strings 要么:使转换操作符或复制构造函数对字符串可重载

Perhaps you'd ask yourself Why would you need a ToString or equiv. 也许你会问自己为什么你需要一个ToString或者等价物。 in the first place? 首先? As some others already noted: the ToString is necessary for introspection (it is called when you hover your mouse over any instance of an object) and the debugger will show it too. 正如其他一些人已经注意到的那样: ToString是内省所必需的(当你将鼠标悬停在任何一个对象实例上时会调用它),调试器也会显示它。 As a programmer, you know that on any non-null object you can safely call ToString , always. 作为程序员,您知道在任何非null对象上,您都可以安全地调用ToString No cast needed, no conversion needed. 无需演员表,无需转换。

It is considered good programming practice to always implement ToString in your own objects with a meaningful value from your persistable properties. 始终在您自己的对象中使用可持久属性中的有意义值实现ToString 是一种很好的编程习惯 Overloads can help if you need different types of representation of your class. 如果您需要不同类型的类表示,则重载可以提供帮助。

More history 更多的历史

If you dive a bit deeper in the history, we see SmallTalk taking a wider approach. 如果你在历史上深入了解一下,我们会看到SmallTalk采取更广泛的方法。 The base object has many more methods, including printString , printOn etc. 基础对象有更多方法,包括printStringprintOn等。

A small decade later, when Bertrand Meyer wrote his landmark book Object Oriented Software construction, he suggested to use a rather wide base class, GENERAL . 十年后,当Bertrand Meyer撰写他的具有里程碑意义的着作“面向对象软件”时,他建议使用相当广泛的基类GENERAL It includes methods like print , print_line and tagged_out , the latter showing all properties of the object, but no default ToString . 它包括printprint_linetagged_out ,后者显示对象的所有属性,但没有默认的ToString But he suggests that the "second base object ANY to which all user defined object derive, can be expanded" , which seems like the prototype approach we now know from JavaScript. 但他建议“所有用户定义的对象派生的第二个基础对象可以扩展” ,这看起来像我们现在从JavaScript中知道的原型方法。

In C++, the only multiple inheritance language still in widespread use, no common ancestor exists for all classes. 在C ++中,唯一仍然广泛使用的多继承语言,并不存在所有类的共同祖先。 This could be the best candidate language to employ your own approach, ie use IStringable . 这可能是使用您自己的方法的最佳候选语言,即使用IStringable But C++ has other ways: you can overload the cast operator and the copy constructor to implement stringability. 但是C ++还有其他方法:你可以重载强制转换操作符和复制构造函数来实现可串行性。 In practice, having to be explicit about a to-string-implementation (as you suggest with IStringable ) becomes quite cumbersome. 在实践中,必须明确关于字符串实现(正如您对IStringable建议)变得非常麻烦。 C++ programmers know that. C ++程序员知道这一点。

In Java we find the first appearance of toString for a mainstream language. 在Java中,我们发现主流语言的toString首次出现。 Unfortunately, Java has two main types: objects and value types. 不幸的是,Java有两种主要类型:对象和值类型。 Value types do not have a toString method, instead you need to use Integer.toString or cast to the object counterpart. 值类型没有toString方法,而是需要使用Integer.toString或强制转换为对象。 This has proven very cumbersome throughout the years, but Java programmers (incl. me) learnt to live with it. 事实证明,这些年来非常繁琐,但Java程序员(包括我)学会了使用它。

Then came C# (I skipped a few languages, don't want to make it too long), which was first intended as a display language for the .NET platform, but proved very popular after initial skepticism. 然后来了C#(我跳过几种语言,不想让它太长),这是最初用作.NET平台的显示语言,但在最初的怀疑之后证明非常受欢迎。 The C# designers (Anders Hejlsberg et al) looked mainly at C++ and Java and tried to take the best of both worlds. C#设计师(Anders Hejlsberg等人)主要研究C ++和Java,并试图充分利用这两个领域。 The value type remained, but boxing was introduced. 值类型仍然存在,但引入了拳击。 This made it possible to have value types derive from Object implicitly. 这使得可以隐式地从Object派生值类型。 Adding ToString analogous to Java was just a small step and was done to ease the transition from the Java world, but has shown its invaluable merits by now. 添加类似于Java的ToString只是一小步,旨在简化从Java世界的过渡,但现在已经显示出其宝贵的价值。

Oddity 怪人

Though you don't directly ask about it, but why would the following have to fail? 虽然你没有直接询问它,但为什么以下必须失败?

object o = null;
Console.WriteLine(o.ToString());

and while you think about it, consider the following, which does not fail: 当你考虑它时,请考虑以下,但不会失败:

public static string MakeString(this object o)
{ return o == null ? "null" : o.ToString();  }

// elsewhere:
object o = null;
Console.WriteLine(o.MakeString());

which makes me ask the question: would, if the language designers had thought of extension methods early on, the ToString method be part of the extension methods to prevent unnecessary NullPointerExceptions? 这让我提出一个问题:如果语言设计者早期想到了扩展方法,那么ToString方法是扩展方法的一部分,以防止不必要的NullPointerExceptions? Some consider this bad design, other consider it a timesaver. 有些人认为这是一个糟糕的设

Eiffel, at the time, had a special class NIL which represented nothingness, but still had all the base class's methods. 埃菲尔当时有一个特殊的类NIL代表虚无,但仍然拥有所有基类的方法。 Sometimes I wished that C# or Java had abandoned null altogether, just like Bertrand Meyer did. 有时我希望C#或Java完全放弃null,就像Bertrand Meyer那样。

Conclusion 结论

The wide approach of classical languages like Eiffel and Smalltalk has been replaced by a very narrow approach. 像Eiffel和Smalltalk这样的经典语言的广泛方法已被一种非常狭隘的方法所取代。 Java still has a lot of methods on Object, C# only has a handful. Java在Object上仍有很多方法,C#只有少数几个。 This is of course good for implementations. 这当然有利于实现。 Keeping ToString in the package simply keeps programming clean and understandable at the same time and because it is virtual , you can (and should!) always override it, which will make your code better apprehendable. 在程序包中保持ToString只是让程序保持清晰和可理解的同时,因为它是virtual ,你可以(而且应该!)总是覆盖它,这将使你的代码更容易被理解。

-- Abel -- - 阿贝尔 -

EDIT: the asker edited the question and made a comparison to IComparable , same is probably true for ICloneable . 编辑:提问者编辑了问题并与IComparable进行了比较, ICloneable可能也是如此。 Those are very good remarks and it is often considered that IComparable should've been included in Object . 这些都是非常好的评论,通常认为IComparable应该包含在Object In line with Java, C# has Equals and not IComparable , but against Java, C# does not have ICloneable (Java has clone() ). 与Java一致,C#具有Equals而不是IComparable ,但是对于Java,C#没有ICloneable (Java有clone() )。

You also state that it is handy for debugging only. 您还声明它仅适用于调试。 Well, consider this everywhere you need to get the string version of something (contrived, no ext. methods, no String.Format, but you get the idea): 好吧,考虑到你需要获得某些东西的字符串版本(设计,没有ext。方法,没有String.Format,但你明白了):

CarInfo car = new CarInfo();
BikeInfo bike = new BikeInfo();
string someInfoText = "Car " +
   (car is IStringable) ? ((IStringable) car).ToString() : "none") +
   ", Bike " + 
   (bike is IStringable) ? ((IStringable) bike).ToString() : "none");

and compare that with this. 并将其与此进行比较。 Whichever you find easier you should choose: 无论您发现哪个更容易,您都应该选择:

CarInfo car = new CarInfo();
BikeInfo bike = new BikeInfo();
string someInfoText = "Car " + car.ToString() + ", Bike " + bike.ToString();

Remember that languages are about making things clearer and easier. 请记住,语言是让事情更清晰,更容易。 Many parts of the language (LINQ, extension methods, ToString() , the ?? operator) are created as conveniences. 语言的许多部分(LINQ,扩展方法, ToString()??运算符)都是作为便利创建的。 None of these are necessities, but sure are we glad that we have them. 这些都不是必需品,但我们很高兴我们拥有它们。 Only when we know how to use them well, we also find the true value of a feature (or not). 只有当我们知道如何使用它们时,我们才能找到特征的真正价值(或不是)。

I'd like to add a couple of thoughts on why .NET's System.Object class definition has a ToString() method or member function, in addition to the previous postings on debugging. 我想补充一些关于为什么.NET的System.Object类定义具有ToString()方法或成员函数的想法,以及之前的调试发布。

Since the .NET Common Language Runtime (CLR) or Execution Runtime supports Reflection, being able to instantiate an object given the string representation of the class type seems to be essential and fundamental. 由于.NET公共语言运行时(CLR)或执行运行时支持Reflection,因此能够在给定类类型的字符串表示的情况下实例化对象似乎是必不可少且基本的。 And if I'm not mistaken, all reference values in the CLR are derived from System.Object, having the ToString() method in the class ensures its availability and usage through Reflection. 如果我没弄错的话,CLR中的所有引用值都是从System.Object派生的,类中的ToString()方法通过Reflection确保其可用性和用法。 Defining and implementing an interface along the lines of IStringable, is not mandatory or required when defining a class in .NET, and would not ensure the ability to dynamically create a new instance after querying an assembly for its supported class types. 在.NET中定义类时,定义和实现IStringable接口并不是必需的,也不是必需的,并且无法确保在查询程序集以查找其支持的类类型后动态创建新实例的能力。

As more advanced .NET functionality available in the 2.0, 3.0 and 3.5 runtimes, such as Generics and LINQ, are based on Reflection and dynamic instantiation, not to mention .NET's Dynamic Language Runtime (DLR) support that allow for .NET implementations of scripting languages, such as Ruby and Python, being able to identify and create an instance by a string type seems to be an essential and indispensable function to have in all class definitions. 由于2.0,3.0和3.5运行时提供的更高级的.NET功能(如Generics和LINQ)基于Reflection和动态实例化,更不用说.NET的动态语言运行时(DLR)支持允许.NET实现脚本能够通过字符串类型识别和创建实例的语言(如Ruby和Python)似乎是所有类定义中必不可少的必不可少的函数。

In short, if we can't identify and name a specific class we want to instantiate, how can we create it? 简而言之,如果我们无法识别和命名我们想要实例化的特定类,我们如何创建它? Relying on a ToString() method that has the base class behavior of returning the Class Type as a "human readable" string seems to make sense. 依赖于具有将类类型作为“人类可读”字符串返回的基类行为的ToString()方法似乎是有意义的。

Maybe a review of the articles and books from Jeffrey Ricther and Don Box on the .NET Framework design and architecture may provide better insights on this topic as well. 也许对Jeffrey Ricther和Don Box关于.NET Framework设计和体系结构的文章和书籍的评论也可以提供关于该主题的更好的见解。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM