常規的調用ToString()方法,存在兩個問題.編程
(1)、調用者沒法控制字符串的格式ide
(2)、調用者不能方便的選擇一種特定的語言文化來格式化字符串.工具
在開發一些國際化的應用時,應用程序須要調用與當前線程不一樣的語言文化來格式化字符串.ui
so,爲了對字符串進行更多的控制,你重寫的的ToString()方法應該容許指定具體的格式和語言文化信息.this
爲了能使調用者在調用對象實例的ToString()方法的時候,選擇格式和語言文化,該對象應該實現System.IFormattable接口,接口代碼以下:spa
// // 摘要: // 提供一種功能,用以將對象的值格式化爲字符串表示形式。 [ComVisible(true)] public interface IFormattable { // // 摘要: // 使用指定格式對當前實例的值設置格式。 // // 參數: // format: // 要使用的格式。 - 或 - null 引用(在 Visual Basic 中爲 Nothing),用於使用爲 System.IFormattable 實現的類型定義的默認格式。 // // formatProvider: // 要用於對值設置格式的提供程序。 - 或 - null 引用(在 Visual Basic 中爲 Nothing),用於從操做系統的當前區域設置獲取數字格式信息。 // // 返回結果: // 採用指定格式的當前實例的值。 string ToString(string format, IFormatProvider formatProvider);
}
注:操作系統
format參數,至關於一個字符串模板,它會解析裏面的字母,並對其進行相應的轉換.如:g表明常規線程
formatProvider參數:指定對應類型的格式化信息,通常和語言文化類型有關3d
FCL(Framework Common Language)中的全部基類型(Byte,SByte,Int16/UInt16,Int32/Uint32,Int64/Uint64,Single,Double,Decimal和Datetime)都實現了這個接口,這些基類型調用ToString方法以後,返回的都是字面值的字符串形式,此外FCL中還有一些類型實現了這個接口.code
一、Guid,Guid的ToString代碼以下所示:
Guid是實現IFormattable接口,具體的實現以下:
public unsafe string ToString(string format, IFormatProvider provider) { string str; if ((format == null) || (format.Length == 0)) { format = "D"; } int offset = 0; bool flag = true; bool flag2 = false; if (format.Length != 1) { throw new FormatException(Environment.GetResourceString("Format_InvalidGuidFormatSpecification")); } char ch = format[0]; switch (ch) { case 'D': case 'd': str = string.FastAllocateString(0x24); break; case 'N': case 'n': str = string.FastAllocateString(0x20); flag = false; break; case 'B': case 'b': str = string.FastAllocateString(0x26); fixed (char* str2 = ((char*) str)) { char* chPtr = str2; if (chPtr != null) { chPtr += RuntimeHelpers.OffsetToStringData; } chPtr[offset++] = '{'; chPtr[0x25] = '}'; } break; case 'P': case 'p': str = string.FastAllocateString(0x26); fixed (char* str3 = ((char*) str)) { char* chPtr2 = str3; if (chPtr2 != null) { chPtr2 += RuntimeHelpers.OffsetToStringData; } chPtr2[offset++] = '('; chPtr2[0x25] = ')'; } break; default: if ((ch != 'X') && (ch != 'x')) { throw new FormatException(Environment.GetResourceString("Format_InvalidGuidFormatSpecification")); } str = string.FastAllocateString(0x44); fixed (char* str4 = ((char*) str)) { char* chPtr3 = str4; if (chPtr3 != null) { chPtr3 += RuntimeHelpers.OffsetToStringData; } chPtr3[offset++] = '{'; chPtr3[0x43] = '}'; } flag = false; flag2 = true; break; } fixed (char* str5 = ((char*) str)) { char* guidChars = str5; if (guidChars != null) { guidChars += RuntimeHelpers.OffsetToStringData; } if (flag2) { guidChars[offset++] = '0'; guidChars[offset++] = 'x'; offset = HexsToChars(guidChars, offset, this._a >> 0x18, this._a >> 0x10); offset = HexsToChars(guidChars, offset, this._a >> 8, this._a); guidChars[offset++] = ','; guidChars[offset++] = '0'; guidChars[offset++] = 'x'; offset = HexsToChars(guidChars, offset, this._b >> 8, this._b); guidChars[offset++] = ','; guidChars[offset++] = '0'; guidChars[offset++] = 'x'; offset = HexsToChars(guidChars, offset, this._c >> 8, this._c); guidChars[offset++] = ','; guidChars[offset++] = '{'; offset = HexsToChars(guidChars, offset, this._d, this._e, true); guidChars[offset++] = ','; offset = HexsToChars(guidChars, offset, this._f, this._g, true); guidChars[offset++] = ','; offset = HexsToChars(guidChars, offset, this._h, this._i, true); guidChars[offset++] = ','; offset = HexsToChars(guidChars, offset, this._j, this._k, true); guidChars[offset++] = '}'; } else { offset = HexsToChars(guidChars, offset, this._a >> 0x18, this._a >> 0x10); offset = HexsToChars(guidChars, offset, this._a >> 8, this._a); if (flag) { guidChars[offset++] = '-'; } offset = HexsToChars(guidChars, offset, this._b >> 8, this._b); if (flag) { guidChars[offset++] = '-'; } offset = HexsToChars(guidChars, offset, this._c >> 8, this._c); if (flag) { guidChars[offset++] = '-'; } offset = HexsToChars(guidChars, offset, this._d, this._e); if (flag) { guidChars[offset++] = '-'; } offset = HexsToChars(guidChars, offset, this._f, this._g); offset = HexsToChars(guidChars, offset, this._h, this._i); offset = HexsToChars(guidChars, offset, this._j, this._k); } } return str; }
查看源代碼發現,Guid的ToString()方法並無使用IFormatProvider參數,緣由是由於,Guid和語言無關,通常用於內部編程使用,因此不須要這個參數.
調用代碼以下:
var gid = Guid.NewGuid(); Console.WriteLine(gid.ToString("d")); Console.WriteLine(gid.ToString("n")); Console.WriteLine(gid.ToString("b")); Console.WriteLine(gid.ToString("p")); Console.WriteLine(gid.ToString("x"));
二、Enum,Enum重寫的ToString()方法,ToString()方法沒有使用到IFormatProvidedr接口,以下所示:
Enum也實現了IFormattable接口,具體實現以下:
public string ToString(string format, IFormatProvider provider) => this.ToString(format);
public string ToString(string format) { if ((format == null) || (format.Length == 0)) { format = "G"; } if (string.Compare(format, "G", StringComparison.OrdinalIgnoreCase) == 0) { return this.ToString(); } if (string.Compare(format, "D", StringComparison.OrdinalIgnoreCase) == 0) { return this.GetValue().ToString(); } if (string.Compare(format, "X", StringComparison.OrdinalIgnoreCase) == 0) { return InternalFormattedHexString(this.GetValue()); } if (string.Compare(format, "F", StringComparison.OrdinalIgnoreCase) != 0) { throw new FormatException(Environment.GetResourceString("Format_InvalidEnumFormatSpecification")); } return InternalFlagsFormat((RuntimeType) base.GetType(), this.GetValue()); }
查看源代碼發現,Enum的ToString()方法並無使用IFormatProvider參數,緣由是由於,Enum和語言無關,通常用於內部編程使用,因此不須要這個參數.
調用代碼以下:
static void Main(string[] args) { var a = Type.a; //返回常規的字符串,也就是a的字符串形式,輸出:a Console.WriteLine(a.ToString("G")); //返回a的枚舉值,輸出:1 Console.WriteLine(a.ToString("D")); //返回a的十六進制表現形式,輸出:00000001 Console.WriteLine(a.ToString("X")); //返回a的字符串形式,輸出:a Console.WriteLine(a.ToString("F")); Console.ReadKey(); } enum Type { a = 1, b = 2, c = 3 }
三、DateTime類型的字符串輸出
由於,不一樣國家的時間展現不同,因此DateTime的字符串輸出必須使用到IFormatProvider參數
DateTime實現了IFormattable接口,因此它能夠自定義地構造咱們想要的DateTime字符串,具體實現以下:
第一步:
DateTimeFormatInfo類實現了IFormatProvider接口.下面是其靜態方法GetInstance()方法的明細:
該方法獲取了傳入IFormatProvider參數的對應語言文化的時間格式化信息(DateTimeFormatInfo)實例.
第二步:
在獲取完對應語言文化的(DateTimeFormatInfo實例)以後,將全部的參數將給DateTimeFormat工具類來處理.其靜態方法Format方法以下:
internal static string Format(DateTime dateTime, string format, DateTimeFormatInfo dtfi, TimeSpan offset) { if ((format == null) || (format.Length == 0)) { bool flag = false; if (dateTime.Ticks < 0xc92a69c000L) { switch (dtfi.Calendar.ID) { case 0x16: case 0x17: case 3: case 4: case 6: case 8: case 13: flag = true; dtfi = DateTimeFormatInfo.InvariantInfo; break; } } if (offset == NullOffset) { if (flag) { format = "s"; } else { format = "G"; } } else if (flag) { format = "yyyy'-'MM'-'ddTHH':'mm':'ss zzz"; } else { format = dtfi.DateTimeOffsetPattern; } } if (format.Length == 1) { format = ExpandPredefinedFormat(format, ref dateTime, ref dtfi, ref offset); } return FormatCustomized(dateTime, format, dtfi, offset); }
該方法將傳入的format進行生成規則的匹配,而後結合語言文化,和日期值,返回一個指望的字符串
(1)、當傳入的format參數只有一個時候:
CLR是這麼處理的,根據傳入的參數獲取對應的日期字符串格式,全部的單個format參數以下:
internal static string GetRealFormat(string format, DateTimeFormatInfo dtfi) { switch (format[0]) { case 'D': return dtfi.LongDatePattern; case 'F': return dtfi.FullDateTimePattern; case 'G': return dtfi.GeneralLongTimePattern; case 'M': case 'm': return dtfi.MonthDayPattern; case 'O': case 'o': return "yyyy'-'MM'-'dd'T'HH':'mm':'ss.fffffffK"; case 'R': case 'r': return dtfi.RFC1123Pattern; case 'T': return dtfi.LongTimePattern; case 'U': return dtfi.FullDateTimePattern; case 'd': return dtfi.ShortDatePattern; case 'f': return (dtfi.LongDatePattern + " " + dtfi.ShortTimePattern); case 'g': return dtfi.GeneralShortTimePattern; case 'Y': case 'y': return dtfi.YearMonthPattern; case 's': return dtfi.SortableDateTimePattern; case 't': return dtfi.ShortTimePattern; case 'u': return dtfi.UniversalSortableDateTimePattern; } throw new FormatException(Environment.GetResourceString("Format_InvalidString")); }
根據傳入的單個參數,CLR獲取其對應的日期格式展現參數,
最後將其和日期值結合,生成對應的StringBuilder對象,並對其進行輸出,後續的代碼由於太長,因此不展現原理就是如此,隨後返回一個指望的字符串值.
調用代碼以下:
static void Main(string[] args) { var dateFlag = new String[] { "G", "d", "D", "g", "M", "m", "s", "T", "t", "u", "U" , "Y" , "r" , "R" , "o" , "O" , "F" , "f" }; var now = DateTime.Now; for (var i = 0; i < dateFlag.Length; i++) { var flag = dateFlag[i]; Console.WriteLine(flag+" 對應的日期生成規則的輸出是:{0}", now.ToString(flag)); } Console.ReadKey(); }
(2)、當傳入的format參數是個字符串的時候
CLR會根據傳入的參數值逐個解析,可是遵循如下規則:
yyyy-表明年份
dd-表明日
MM-表明月份
HH:表明當前小時
mm:表明當前分鐘
ss:表明當前秒
g:表明公元
這些標誌會被CLR正確解析成對應的字段,其他的字符會被CLR當作分隔符留用,代碼以下:
var now = DateTime.Now; Console.WriteLine(now.ToString("gyyyy分MM隔HH:mm:ss"));
四、IFormattable接口實現方法參數解析
(1)、IFormatProvider參數
DateTime默認的ToString()方法
DateTimeFormatInfo.CurrentInfo代碼以下:
能夠,看出,不給ToString()方法傳遞IFormatProvider參數,CLR會默認採用當前線程的DateTimeFormatInfo對象實例.
注:FCL中實現IFormatProvider的接口只有三個,分別是
這些類中存在一些構造並格式化字符串時,必要的屬性信息(按語言區分).
五、輸出一個德國的時間字符串
var now = DateTime.Now; //按照德文輸出當前時間 g-表明公元開始時間 Console.WriteLine(now.ToString("gyyyy:MM:dd HH:mm:ss",new CultureInfo("de-DE")));