.NET 格式化系統允許開發者通過實現 IFormatProvider 和 ICustomFormatter 定義專屬格式字符串(如 {0:MyFormat}),實現業務專屬的輸出格式。
提供格式化服務的工廠,單一方法:
object GetFormat(Type formatType);
formatType:請求的格式化器類型(通常是 typeof(ICustomFormatter))null 表示不支持實際執行格式化,單一方法:
string Format(string format, object arg, IFormatProvider formatProvider);
format:格式字符串(冒號後內容,如 "MyFormat")arg:待格式化的對象formatProvider:格式化提供者(可遞歸調用)public class MyFormatter : ICustomFormatter, IFormatProvider {
// IFormatProvider:被請求時返回自己(如果是 ICustomFormatter)
public object GetFormat(Type formatType) {
return formatType == typeof(ICustomFormatter) ? this : null;
}
// ICustomFormatter:核心格式化邏輯
public string Format(string format, object arg, IFormatProvider provider) {
// 1. 無自定義格式 → 走標準格式化
if (string.IsNullOrEmpty(format)) {
return arg is IFormattable
? ((IFormattable)arg).ToString(null, provider)
: arg.ToString();
}
// 2. 識別自定義格式字符串
if (format == "MyFormatter") {
return "**" + arg.ToString(); // 業務邏輯:前綴 **
}
// 3. 其他格式 → 委託標準格式化
return arg is IFormattable
? ((IFormattable)arg).ToString(format, provider)
: arg.ToString();
}
}
int i = 100;
MyFormatter fmt = new MyFormatter();
// 三種調用方式
string s1 = string.Format(fmt, "{0}", i); // 無格式 → "100"
string s2 = string.Format(fmt, "{0:C}", i); // 標準貨幣 → "¥100.00"
string s3 = string.Format(fmt, "{0:MyFormatter}", i); // 自定義 → "**100"
string.Format(provider, format, args) 接收 MyFormatter 作為 providerStringBuilder.AppendFormat 解析格式字符串{0:MyFormatter},調用 provider.GetFormat(typeof(ICustomFormatter))MyFormatter 實例,調用其 Format("MyFormatter", 100, provider)"**100",拼接進最終字符串if (format == "HEX") return "0x" + ((int)arg).ToString("X");
if (format == "BIN") return "0b" + Convert.ToString((int)arg, 2);
if (arg is User u && format == "SHORT")
return $"{u.Name}({u.Id})";
IFormatProvider 實例通常無狀態,可單例複用Format 中做重 IO/計算,必要時緩存public class User : IFormattable {
public string ToString(string format, IFormatProvider provider) {
if (provider is MyFormatter)
return ((MyFormatter)provider).Format(format, this, provider);
return base.ToString();
}
}
| 問題 | 原因 | 解決 |
|---|---|---|
| 自定義格式不生效 | GetFormat 返回 null 或類型不匹配 | 確保返回 this 且參數為 typeof(ICustomFormatter) |
| 遞歸調用導致棧溢出 | Format 中又調用 string.Format 且未終止條件 | 非自定義格式直接委託 arg.ToString(format, provider) |
| 數值類型格式化異常 | arg 為值類型,boxing/unboxing 開銷 | 用泛型或模式匹配優化 |
IFormatProvider 是「工廠」,ICustomFormatter 是「工人」。兩者合一實現自定義格式化:GetFormat 識別身份,Format 實現邏輯。掌握調用鏈路 string.Format → AppendFormat → GetFormat → Format,即可為任何類型注入業務專屬格式語義,極大提升代碼表達力與可讀性。