讀【157個完美化C#的建議】一書的理解筆記 - 013
重點:讓類別的輸出格式更具意義
流程說明 |
1. 類別複寫.Tostring()範例 |
2. 彈性的格式化器 |
3. 結合.Tostring() 與 格式化器 |
4. 結論 |
1. 類別複寫.Tostring()範例
我們先宣告以下,classPerson類別,這邊情境設定為
東方名字 : 姓 + 名
西方名字 : 名 + 姓
/// <summary>
/// classPersonB 類別覆寫 .ToString() 方法
/// </summary>
public class classPerson : IFormattable
{
/// <summary>
/// 身分證
/// </summary>
public string IDCode { get; set; }
/// <summary>
/// 姓
/// </summary>
public string FirstName { get; set; }
/// <summary>
/// 名
/// </summary>
public string LastName { get; set; }
/// <summary>
/// 覆寫classPerson在內部呼叫ToString()的方法
/// </summary>
/// <returns></returns>
public override string ToString()
{
//東方名字 姓 + 名
return string.Format("{0} {1}", this.FirstName, this.LastName);
}
/// <summary>
/// 覆寫classPerson於外部呼叫ToString()的方法
/// </summary>
/// <param name="format"></param>
/// <param name="formatProvider"></param>
/// <returns></returns>
public string ToString(string format, IFormatProvider formatProvider)
{
switch (format)
{
case "Ch":
//東方名字 姓 + 名 ※已於ClassB內部覆寫
return this.ToString();
case "Eg":
//西方名字 名 + 姓
return string.Format("{0} {1}", this.LastName, this.FirstName);
default:
//(預設)東方名字 姓 + 名 ※已於classPerson內部覆寫
return this.ToString();
}
}
}
在主程式中,執行下列語法,完成基本覆寫ToString() 格式化 :
當代入Ch 時 顯示 東方名字 ,Eg時顯示 西方名字
classPerson objB = new classPerson() { IDCode = "H333456789", FirstName = "王", LastName = "小明" };
//Class資訊(類別資訊)
var resultA = objA.ToString();
//王 小明
var resultB = objB.ToString("Ch", null);
//小明 王
var resullC = objB.ToString("Eg", null);
//王 小明
var resullD = objB.ToString();
2. 彈性的格式化器
(彈性好,目的性較低)
假設今天有多個類似"功能"的類別,但每個類別基本上都複寫一次相同的程式碼是滿辛苦的 (※Copy Paste 為工程師最高境界)
因此我們可以利用客製化格式化器 ICustomFormatter 來達到這個效果,寫一次,讓所有類別可以一起使用
宣告以下:
/// <summary>
/// 實作"格式化器" - 自訂義套用的格式化字串方法
/// </summary>
public class classPersonFormatter : IFormatProvider, ICustomFormatter
{
/// <summary>
/// 說明:IFormatProvider 實作
/// 目的:傳進來的ICustomFormatter 需求物件,回傳自己
/// 為了能餵進下方的Format(string format, object arg, IFormatProvider formatProvider)
/// 的formatProvider 參數中
/// </summary>
/// <param name="formatType"></param>
/// <returns></returns>
public object GetFormat(Type formatType)
{
if (formatType == typeof(ICustomFormatter))
{
return this;
}
else
{
return null;
}
}
/// <summary>
/// 說明:ICustomFormatter 實作
/// 目的:可以傳進自己想要定義的格式化資料
/// </summary>
/// <param name="format"></param>
/// <param name="arg"></param>
/// <param name="formatProvider"></param>
/// <returns></returns>
public string Format(string format, object arg, IFormatProvider formatProvider)
{
//這邊因為是範例所以簡化,如果有多個類別 可以先用IS判斷再轉到對應的型別,餵進對應的格式化
classPerson person = arg as classPerson;
if (person == null)
{
return string.Empty;
}
switch (format)
{
case "Ch":
return string.Format("{0} {1}", person.FirstName, person.LastName);
case "Eg":
return string.Format("{0} {1}", person.LastName, person.FirstName);
case "ChM":
return string.Format("{0} {1} : {2}", person.FirstName, person.LastName, person.IDCode);
default:
return string.Format("{0} {1}", person.LastName, person.FirstName);
}
}
}
我們建立 ClassPerson 放進基本資料, 並且建立格式化器 classPersonFormatter
//============= 2. 使用格式化器的方法
classPerson objC = new classPerson() { FirstName = "王", LastName = "大明", IDCode = "H333456790" };
classPersonFormatter cpFormatter = new classPersonFormatter();
接著就可以呼叫 格式化器 ==> cpFomatter(傳參 , 型別物件 , null ) 呼叫 對應的格式。
//王 大明
var result_objC_A = objC.ToString();
//王 大明
var result_objC_B = cpFormatter.Format("Ch", objC, null);
//大明 王
var resull_objC_C = cpFormatter.Format("Eg", objC, null);
//王 大明 : H333456790
var resull_objC_D = cpFormatter.Format("ChM", objC, null);
3. 結合.Tostring() 與 格式化器
(目的性明確,彈性變低)
回到第1.項 ,如果以類型本身為出發點,更好的做法是量身訂做,選擇我們今天需要為這個類別使用什麼格式化器,宣告如下:
/// <summary>
/// 結合.Tostring() 與 格式化器
/// </summary>ㄍ
public class classPersonCombination : IFormattable
{
/// <summary>
/// 身分證
/// </summary>
public string IDCode { get; set; }
/// <summary>
/// 姓
/// </summary>
public string FirstName { get; set; }
/// <summary>
/// 名
/// </summary>
public string LastName { get; set; }
/// <summary>
/// 覆寫classPersonCombination 在內部呼叫ToString()的方法
/// </summary>
/// <returns></returns>
public override string ToString()
{
//東方名字 姓 + 名
return string.Format("{0} {1}", this.FirstName, this.LastName);
}
/// <summary>
/// 覆寫classPersonCombination 於外部呼叫ToString()的方法
/// </summary>
/// <param name="format"></param>
/// <param name="formatProvider"></param>
/// <returns></returns>
public string ToString(string format, IFormatProvider formatProvider)
{
switch (format)
{
case "Ch":
//東方名字 姓 + 名 ※已於classPersonCombination內部覆寫
return this.ToString();
case "Eg":
//西方名字 名 + 姓
return string.Format("{0} {1}", this.LastName, this.FirstName);
case "ChM":
//完整資訊
return string.Format("{0} {1} : {2}", this.FirstName, this.LastName, IDCode);
default:
//善用 IFormatProvider 格式化器
classPersonFormatter myCustomFormatter = formatProvider as classPersonFormatter;
//如果沒有格式化器,則回傳我們覆寫的ToString()
if (myCustomFormatter == null)
return this.ToString();
else
{
return myCustomFormatter.Format(format, this, null);
}
}
}
}
在Default 的地方是本範例的關鍵,將格式化器放進去,讓這個類別可以吃指定的格式化器
default:
//善用 IFormatProvider 格式化器
classPersonFormatter myCustomFormatter = formatProvider as classPersonFormatter;
//如果沒有格式化器,則回傳我們覆寫的ToString()
if (myCustomFormatter == null)
return this.ToString();
else
{
return myCustomFormatter.Format(format, this, null);
}
回到主程式,第1.版本的Code ,可以簡化為以下:
//============ 結合 1. 2.的方法
classPersonCombination objD =
new classPersonCombination() { FirstName = "王", LastName = "超明", IDCode = "H333456791" };
classPersonFormatter cp2Formatter = new classPersonFormatter();
//王 超明
var result_objD_A = objD.ToString();
//王 超明
var result_objD_B = objD.ToString("Ch", cp2Formatter);
//超明 王
var resull_objD_C = objD.ToString("Eg", cp2Formatter);
//王 超明 : H333456791
var resull_objD_D = objD.ToString("ChM", cp2Formatter);
4. 結論
應該依照 1.使用者需求 2.開發規範 3.設計情境
來決定最適合的作法 ,甚至不覆寫類別的格式化(省時),才是最佳做法。
OS: 不過如果有前人寫了完善的格式化器,真的很感動 ,在愈大型的系統裡,威力愈強大。
github連結(Vs2015) : 點我下載