簡體   English   中英

為什么object.ToString()存在?

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

擁有一個IStringable接口不是更優雅和整潔嗎?

誰需要這個Type.FullName對象返回給我們?

編輯:每個人都在問為什么我覺得它更優雅..

好吧,它就是這樣,而不是IComparable,對象會有CompareTo方法,默認情況下拋出異常或返回0。

有些對象不能也不應該被描述為字符串。 對象可以同樣返回string.Empty Type.FullName只是一個任意選擇..

對於Console.Write(對象)之類的方法,我認為應該是:Write(IStringable)。

但是,如果你將WriteLine用於除字符串之外的任何東西(或其ToString很明顯的東西,如數字),在我看來它只適用於調試模式..

順便說一句 - 我該怎么評論你們所有人? 我發布答案可以嗎?

恕我直言應該有三種虛擬方法從未添加到System.Object ...

  • 的ToString()
  • GetHashCode的()
  • 等於()

所有這些都可以按照您建議的界面實現。 如果他們這樣做了我認為我們會好得多。 那么為什么這些問題呢? 我們只關注ToString():

  1. 如果ToString()應該由使用ToString()的人實現並顯示結果,那么您將擁有編譯器無法強制執行的隱式契約。 您假設ToString()被重載,但沒有辦法強制這樣做。
  2. 使用IStringable,您只需要將其添加到泛型類型約束或從中派生接口,以要求它在實現對象時使用。
  3. 如果您在重載ToString()時發現的好處是針對調試器,則應該開始使用[System.Diagnostics.DebuggerDisplayAttribute]。
  4. 至於需要通過String.Format()和/或Console.WriteLine將對象轉換為字符串的實現,它們可能已經延遲到System.Convert.ToString(對象)並檢查了類似'IStringable'的內容,故障轉移到如果沒有實現,類型的名稱。
  5. 正如克里斯托弗埃斯特普指出的那樣,它具有特定的文化特征。

所以我想我獨自站在這里說我討厭System.Object及其所有虛擬方法。 但我確實喜歡C#作為一個整體而且整體而言我認為設計師做得很好。

注意:如果您打算依賴於ToString()的重載行為,我建議您繼續定義您的IStringable接口。 不幸的是,如果你真的想要它,你將不得不為該方法選擇另一個名稱。

更多

我的同事和我剛剛談到這個話題。 我認為ToString()的另一個大問題是回答“它用於什么?”的問題。 是顯示文字嗎? 序列化文字? 調試文字? 全姓名?

讓Object.ToString使得像Console.WriteLine這樣的API成為可能。

從設計的角度來看,BCL的設計者認為提供實例的字符串表示的能力應該對所有對象都是通用的。 真正的完整類型名稱並不總是有用,但他們覺得在根級別具有可自定義表示的能力超過了在輸出中看到完整類型名稱的輕微煩惱。

如果沒有Object.ToString,您可以實現Console.WriteLine,而是執行接口檢查,如果接口不存在,則默認為該類型的全名。 但是,每個想要捕獲對象實例的字符串表示的API都必須實現此邏輯。 考慮到在核心BCL中使用Object.ToString的次數,這將導致大量重復。

我想它存在是因為它對所有物體都是非常方便的東西,並且不需要使用add'l cruft。 為什么你認為IStringable會更優雅?

一點也不。

它不需要實現它返回特定於文化的結果。

此方法返回對文化敏感的人類可讀字符串。 例如,對於值為零的Double類的實例,Double .. ::。ToString的實現可能返回“0.00”或“0,00”,具體取決於當前的UI文化。

此外,雖然它有自己的實現,但它可以被覆蓋,而且往往是。

為什么要把它變得更復雜? 它現在的方式基本上確定每個對象都能夠將其值打印到字符串,我看不出有什么問題。

嗯,所以可以在派生類中重寫它?

“可串行”表示在很多場景中都很有用,庫設計者可能認為ToString()更直接。

使用IStringable,您將不得不進行額外的檢查/強制轉換,以查看是否可以以字符串格式輸出對象。 對於這樣一個常見的操作而言,這對於性能的影響太大了,無論如何對99.99%的所有對象都應該是一件好事。

Structs和Objects都具有ToString()成員以便於調試。

最簡單的例子可以在Console.WriteLine中看到,它接收包括object在內的整個類型列表,但也接收params object[] args 由於Console通常是TextWriter的一個層,因此在寫入文件和其他流(套接字)時,這些語句也很有用(有時)。

它還說明了一個簡單的面向對象設計,它表明您不應僅僅因為可以創建接口。

我的新基類:

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

確實沒有將Type.FullName返回給你的Type.FullName ,但是如果返回一個空字符串或null則更少使用它。 你問為什么它存在。 這不是太容易回答,多年來一直是一個備受爭議的問題。 十多年前,幾種新語言決定在需要時隱式地將對象強制轉換為字符串會很方便,這些語言包括Perl,PHP和JavaScript,但它們都沒有完全遵循面向對象的范例。

途徑

面向對象語言的設計者有一個更難的問題。 通常,有三種方法可以獲取對象的字符串表示形式:

  • 使用多重繼承,只需從String繼承,您就可以轉換為字符串
  • 單繼承:將ToString作為虛方法添加到基類
  • 要么:使轉換操作符或復制構造函數對字符串可重載

也許你會問自己為什么你需要一個ToString或者等價物。 首先? 正如其他一些人已經注意到的那樣: ToString是內省所必需的(當你將鼠標懸停在任何一個對象實例上時會調用它),調試器也會顯示它。 作為程序員,您知道在任何非null對象上,您都可以安全地調用ToString 無需演員表,無需轉換。

始終在您自己的對象中使用可持久屬性中的有意義值實現ToString 是一種很好的編程習慣 如果您需要不同類型的類表示,則重載可以提供幫助。

更多的歷史

如果你在歷史上深入了解一下,我們會看到SmallTalk采取更廣泛的方法。 基礎對象有更多方法,包括printStringprintOn等。

十年后,當Bertrand Meyer撰寫他的具有里程碑意義的着作“面向對象軟件”時,他建議使用相當廣泛的基類GENERAL 它包括printprint_linetagged_out ,后者顯示對象的所有屬性,但沒有默認的ToString 但他建議“所有用戶定義的對象派生的第二個基礎對象可以擴展” ,這看起來像我們現在從JavaScript中知道的原型方法。

在C ++中,唯一仍然廣泛使用的多繼承語言,並不存在所有類的共同祖先。 這可能是使用您自己的方法的最佳候選語言,即使用IStringable 但是C ++還有其他方法:你可以重載強制轉換操作符和復制構造函數來實現可串行性。 在實踐中,必須明確關於字符串實現(正如您對IStringable建議)變得非常麻煩。 C ++程序員知道這一點。

在Java中,我們發現主流語言的toString首次出現。 不幸的是,Java有兩種主要類型:對象和值類型。 值類型沒有toString方法,而是需要使用Integer.toString或強制轉換為對象。 事實證明,這些年來非常繁瑣,但Java程序員(包括我)學會了使用它。

然后來了C#(我跳過幾種語言,不想讓它太長),這是最初用作.NET平台的顯示語言,但在最初的懷疑之后證明非常受歡迎。 C#設計師(Anders Hejlsberg等人)主要研究C ++和Java,並試圖充分利用這兩個領域。 值類型仍然存在,但引入了拳擊。 這使得可以隱式地從Object派生值類型。 添加類似於Java的ToString只是一小步,旨在簡化從Java世界的過渡,但現在已經顯示出其寶貴的價值。

怪人

雖然你沒有直接詢問它,但為什么以下必須失敗?

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

當你考慮它時,請考慮以下,但不會失敗:

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

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

這讓我提出一個問題:如果語言設計者早期想到了擴展方法,那么ToString方法是擴展方法的一部分,以防止不必要的NullPointerExceptions? 有些人認為這是一個糟糕的設

埃菲爾當時有一個特殊的類NIL代表虛無,但仍然擁有所有基類的方法。 有時我希望C#或Java完全放棄null,就像Bertrand Meyer那樣。

結論

像Eiffel和Smalltalk這樣的經典語言的廣泛方法已被一種非常狹隘的方法所取代。 Java在Object上仍有很多方法,C#只有少數幾個。 這當然有利於實現。 在程序包中保持ToString只是讓程序保持清晰和可理解的同時,因為它是virtual ,你可以(而且應該!)總是覆蓋它,這將使你的代碼更容易被理解。

- 阿貝爾 -

編輯:提問者編輯了問題並與IComparable進行了比較, ICloneable可能也是如此。 這些都是非常好的評論,通常認為IComparable應該包含在Object 與Java一致,C#具有Equals而不是IComparable ,但是對於Java,C#沒有ICloneable (Java有clone() )。

您還聲明它僅適用於調試。 好吧,考慮到你需要獲得某些東西的字符串版本(設計,沒有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");

並將其與此進行比較。 無論您發現哪個更容易,您都應該選擇:

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

請記住,語言是讓事情更清晰,更容易。 語言的許多部分(LINQ,擴展方法, ToString()??運算符)都是作為便利創建的。 這些都不是必需品,但我們很高興我們擁有它們。 只有當我們知道如何使用它們時,我們才能找到特征的真正價值(或不是)。

我想補充一些關於為什么.NET的System.Object類定義具有ToString()方法或成員函數的想法,以及之前的調試發布。

由於.NET公共語言運行時(CLR)或執行運行時支持Reflection,因此能夠在給定類類型的字符串表示的情況下實例化對象似乎是必不可少且基本的。 如果我沒弄錯的話,CLR中的所有引用值都是從System.Object派生的,類中的ToString()方法通過Reflection確保其可用性和用法。 在.NET中定義類時,定義和實現IStringable接口並不是必需的,也不是必需的,並且無法確保在查詢程序集以查找其支持的類類型后動態創建新實例的能力。

由於2.0,3.0和3.5運行時提供的更高級的.NET功能(如Generics和LINQ)基於Reflection和動態實例化,更不用說.NET的動態語言運行時(DLR)支持允許.NET實現腳本能夠通過字符串類型識別和創建實例的語言(如Ruby和Python)似乎是所有類定義中必不可少的必不可少的函數。

簡而言之,如果我們無法識別和命名我們想要實例化的特定類,我們如何創建它? 依賴於具有將類類型作為“人類可讀”字符串返回的基類行為的ToString()方法似乎是有意義的。

也許對Jeffrey Ricther和Don Box關於.NET Framework設計和體系結構的文章和書籍的評論也可以提供關於該主題的更好的見解。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM