您不能不知的ToString()方法
1.1.1 摘要
相信大家對ToString()方法再熟悉不過了,由于該方法是.NET程序中最常用的方法之一,我們除了可以直接調用ToString()方法之外,.NET中的某些方法也隱式調用ToString()方法(WPF,Windows Form和Silverlight等)。
1.1.2 正文
首先讓我們了解一下ToString()的由來,它是由Object類提供一個虛的方法,ToString()方法返回一個字符串顯示調用該方法對象的類型,Object類中實現如下。
////Object中的ToString()實現 public virtual string ToString() { return this.GetType().ToString(); }
現在我們知道Object類提供的是一個虛的ToString()方法,表明.NET也提供我們重寫ToString()方法,接來下讓我們定義一個Customer類,然后再使用Console.WriteLine()方法隱式調用Customer類的ToString()方法輸出,注意我們現在還沒有重寫ToString方法,所以我們調用的是Object類的ToString()方法。
/// <summary> /// Customer has three properities /// Name, Revenum and Tel. /// </summary> public class Customer { private string _name; private decimal _revenue; private string _tel; private string _zip; /// <summary> /// Initializes a new instance of the <see cref="Customer"/> class. /// </summary> public Customer(string name, decimal revenue, string tel, string zip) { this.Name = name; this.Revenue = revenue; this.Tel = tel; this.Zip = zip; } /// <summary> /// Gets or sets the name. /// </summary> /// <value> /// The name. /// </value> public string Name { get { return _name; } set { _name = value; } } /// <summary> /// Gets or sets the revenue. /// </summary> /// <value> /// The revenue. /// </value> public decimal Revenue { get { return _revenue; } set { _revenue = value; } } /// <summary> /// Gets or sets the tel. /// </summary> /// <value> /// The tel. /// </value> public string Tel { get { return _tel; } set { _tel = value; } } /// <summary> /// Gets or sets the zip. /// </summary> /// <value> /// The zip. /// </value> public string Zip { get { return _zip; } set { _zip = value; } } }
圖1輸出類型字符串
重新ToString()方法
通過上圖我們可以很明確的說明了在沒有重新ToString()方法時,我們調用的是父類Object的ToString()方法。不但它的輸出不怎么make sence,而且它并沒有輸出我們需要的值,那么接下來讓我們重寫ToString()方法。
public override string ToString() { //// Implemention Code. }
上面我們重寫了ToString()方法返回Name,Revenuehe和Tel屬性,接著我們使用WriteLine()方法隱式調用ToString()方法(.NET提供顯式和隱式調用ToString()的方法,如:Console.WriteLine、String.Format和.NET控件等)。
雖然簡單的ToString()方法很多時候已經可以滿足我們的需求,但有時候我們還需要功能更強的方法來格式化輸出字符串。(如:電話號碼,日期和郵編等格式)
要實現上述格式,我們可以通過實現IFormattable 接口來解決這個問題。 IFormattable 接口包含了一個重載的ToString()方法,它允許我們為類型指定某種格式信息。當我們需要為類型創建不同形式的字符串輸出時,實現這個接口就顯得很有用了。
-
實現IFormattable接口
現在我們修改一下我們的Customer類,讓它實現IFormattable接口,然后重寫ToString()方法,實現如下:
/// <summary> /// Customer has three properities /// Name, Revenum and Tel. /// </summary> public class Customer : IFormattable { private string _name; private decimal _revenue; private string _tel; private string _zip; /// <summary> /// Initializes a new instance of the <see cref="Customer"/> class. /// </summary> public Customer(string name, decimal revenue, string tel, string zip) { this.Name = name; this.Revenue = revenue; this.Tel = tel; this.Zip = zip; } /// <summary> /// Gets or sets the name. /// </summary> /// <value> /// The name. /// </value> public string Name { get { return _name; } set { _name = value; } } /// <summary> /// Gets or sets the revenue. /// </summary> /// <value> /// The revenue. /// </value> public decimal Revenue { get { return _revenue; } set { _revenue = value; } } /// <summary> /// Gets or sets the tel. /// </summary> /// <value> /// The tel. /// </value> public string Tel { get { return _tel; } set { _tel = value; } } /// <summary> /// Gets or sets the zip. /// </summary> /// <value> /// The zip. /// </value> public string Zip { get { return _zip; } set { _zip = value; } } #region IFormattable 成員 /// <summary> /// Override ToString method, /// and custom output string format. /// </summary> /// <param name="format"></param> /// <param name="formatProvider"></param> /// <returns></returns> public string ToString(string format, IFormatProvider formatProvider) { if (formatProvider != null) { ICustomFormatter fmt = formatProvider.GetFormat(this.GetType()) as ICustomFormatter; if (fmt != null) { return fmt.Format(format, this, formatProvider); } } switch (format) { case "LC": return string.Format("Name: {0}\nRevenue: {1:C2}\nTel: {2}\nZip: {3}\n", Name.ToLower(), Revenue, Tel.Replace("_", "-"), Zip.Replace("_", "")); case "UC": return string.Format("Name: {0}\nRevenue: {1:C3}\nTel: {2}\nZip: {3}\n", Name.ToUpper(), Revenue, Tel.Replace("_", ":"), Zip.Replace("_", "")); case "G": return string.Format("Name: {0}\nRevenue: {1}\nTel: {2}\nZip: {3}\n", Name, Revenue, Tel, Zip); default: return ""; } } #endregion }
class Program { /// <summary> /// 客戶端調用ToString()方法 /// </summary> /// <param name="args"></param> static void Main(string[] args) { Customer objCustomer = new Customer("JK_Rush", 8888, "0723_98765423", "65_4321"); Console.WriteLine(objCustomer.ToString("LC", null)); Console.ReadKey(); } }
圖2 IFormattable的實現
使我們設計符合OCP
現在我們的Customer類實現了IFormattable接口,然后重寫了ToString()方法,接著提供三種格式化類型輸出,OK現在我們的輸出字符串有更好的自定義格式了,但用戶需求的格式是在不斷的變化,我們無法估計和預知用戶需求的輸出格式,難道每當遇到不符合用戶要求的格式我們都要在switch中添加嗎?我們的確可以這樣做,這種做法看上去是直截了當,但這不符合設計模式的OCP原則。幸運的是.NET提供了符合OCP原則的實現方法,通過實現IFormattable接口使得實現該接口的類對StringFomat()修改關閉,當實現ICustomFormatter接口的類對StringFomat()擴展開放。
“Software entities (classes, modules, functions, etc.) should be open for extension,
but closed for modification [Martin, p.99]”
現在我們添加自定義類CustomFormatProvider,通過它可以擴展Customer的輸出字符串的格式類型,而且避免了對Customer類的修改遵守了OCP,然后再實現接口IFormatProvider和ICustomFormatter,在Format()方法中實現字符串輸出格式。
/// <summary> /// Custom string format provider. /// </summary> public class CustomFormatProvider : IFormatProvider, ICustomFormatter { // Methods #region IFormatProvider 成?員? /// <summary> /// Get format provider object. /// </summary> /// <param name="formatType"></param> /// <returns>The CustomFormatProvider object.</returns> public object GetFormat(Type formatType) { if (formatType == typeof(ICustomFormatter)) { return new CustomFormatProvider(); } return null; } #endregion #region ICustomFormatter 成?員? public string Format(string format, object arg, IFormatProvider formatProvider) { Customer c = arg as Customer; if (c != null) { //// Custom string output format. switch (format) { case "custom": return string.Format("Name: {0}\nRevenue: {1:C4}\nTel: {2}\nZip: {3}\n", c.Name.ToUpper(), c.Revenue, c.Tel.Replace("_", ":"), c.Zip.Replace("_", "-")); default: return ""; } } return ""; } #endregion }
class Program { /// <summary> /// 客戶端調用ToString()方法 /// </summary> /// <param name="args"></param> static void Main(string[] args) { Customer objCustomer = new Customer("JK_Rush", 8888, "0723_98765423", "65_4321");
Console.WriteLine(string.Format( new CustomFormatProvider(), "{0:custom}", objCustomer));
Console.ReadKey(); } }
圖4自定義字符串格式
圖5自定義格式實現框架
上面的GetFormat()方法創建一個實現ICustomFormatter接口的對象,而且通過重寫Format()方法來自定義輸出字符串格式,通過傳遞不同format參數以便指定不同格式選項。
1.1.3 總結
不管一個類是否實現了IFormattable接口,我們都可以為其創建 IFormatProvider 和 ICustomFormatter 的實現類。因此即使一個類的原來沒有提供合理的ToString()行為,我們仍然可以為其提供格式化支持。當然,作為一個類的外部訪問者,我們只能通過訪問其中的公有屬性和數據成員來構造字符串。雖然編寫格式提供者類(分別實現IFormatProvider 和 ICustomFormatter)需要很多工作,且其目的僅僅是為了得到一個字符串。但是,一旦使用了這種方式來實現我們自己定義的字符串輸出,它們將在.NET 框架的各個地方得到支持。
現在,再讓我們回到類這一角色上來。重寫 Object.ToString()是為類提供字符串表示的最簡單方式。每當我們創建一個類型時,都要提供該方法。它應該是我們的類型最明顯、最常用的一種表示。而且只有在一些比較少的情況下,當我們期望為類型提供更復雜的輸出格式時,才應該實現IFormattable 接口。它為“類型的用戶定制類型的字符 串輸出”提供了一種標準的方式。如果我們沒有做這些工作,用戶就要自己來實現自定義格式化器。那樣的做法需要更多的代碼,因為用戶處于類外,無法訪問對象 的內部狀態。
|
|
關于作者:[作者]:
JK_Rush從事.NET開發和熱衷于開源高性能系統設計,通過博文交流和分享經驗,歡迎轉載,請保留原文地址,謝謝。 |

相信大家對ToString()方法再熟悉不過了,而且關于ToString()的一些您不能不知道,由于該方法是.NET程序中最常用的方法之一,我們除了可以直接調用ToString()方法之外,.NET中的某些方法也隱式調用ToString()方法(WPF,Windows Form和Silverlight等)。



浙公網安備 33010602011771號