常规的调用ToString()方法,存在两个问题.

(1)、调用者无法控制字符串的格式

(2)、调用者不能方便的选择一种特定的语言文化来格式化字符串.

在开发一些国际化的应用时,应用程序需要调用与当前线程不同的语言文化来格式化字符串.

so,为了对字符串进行更多的控制,你重写的的ToString()方法应该允许指定具体的格式和语言文化信息.

为了能使调用者在调用对象实例的ToString()方法的时候,选择格式和语言文化,该对象应该实现System.IFormattable接口,接口代码如下:

    //
// 摘要:
// 提供一种功能,用以将对象的值格式化为字符串表示形式。
[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参数:指定对应类型的格式化信息,一般和语言文化类型有关

FCL(Framework Common Language)中的所有基类型(Byte,SByte,Int16/UInt16,Int32/Uint32,Int64/Uint64,Single,Double,Decimal和Datetime)都实现了这个接口,这些基类型调用ToString方法之后,返回的都是字面值的字符串形式,此外FCL中还有一些类型实现了这个接口.

1、Guid,Guid的ToString代码如下所示:

Guid是实现IFormattable接口,具体的实现如下:

public unsafe string ToString(string format, IFormatProvider provider)
{
string str;
if ((format == null) || (format.Length == ))
{
format = "D";
}
int offset = ;
bool flag = true;
bool flag2 = false;
if (format.Length != )
{
throw new FormatException(Environment.GetResourceString("Format_InvalidGuidFormatSpecification"));
}
char ch = format[];
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++] = '';
guidChars[offset++] = 'x';
offset = HexsToChars(guidChars, offset, this._a >> 0x18, this._a >> 0x10);
offset = HexsToChars(guidChars, offset, this._a >> , this._a);
guidChars[offset++] = ',';
guidChars[offset++] = '';
guidChars[offset++] = 'x';
offset = HexsToChars(guidChars, offset, this._b >> , this._b);
guidChars[offset++] = ',';
guidChars[offset++] = '';
guidChars[offset++] = 'x';
offset = HexsToChars(guidChars, offset, this._c >> , 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 >> , this._a);
if (flag)
{
guidChars[offset++] = '-';
}
offset = HexsToChars(guidChars, offset, this._b >> , this._b);
if (flag)
{
guidChars[offset++] = '-';
}
offset = HexsToChars(guidChars, offset, this._c >> , 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"));

2、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 == ))
{
format = "G";
}
if (string.Compare(format, "G", StringComparison.OrdinalIgnoreCase) == )
{
return this.ToString();
}
if (string.Compare(format, "D", StringComparison.OrdinalIgnoreCase) == )
{
return this.GetValue().ToString();
}
if (string.Compare(format, "X", StringComparison.OrdinalIgnoreCase) == )
{
return InternalFormattedHexString(this.GetValue());
}
if (string.Compare(format, "F", StringComparison.OrdinalIgnoreCase) != )
{
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 = ,
b = ,
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 == ))
{
bool flag = false;
if (dateTime.Ticks < 0xc92a69c000L)
{
switch (dtfi.Calendar.ID)
{
case 0x16:
case 0x17:
case :
case :
case :
case :
case :
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 == )
{
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[])
{
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 = ; 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"));

4、IFormattable接口实现方法参数解析

(1)、IFormatProvider参数

DateTime默认的ToString()方法

DateTimeFormatInfo.CurrentInfo代码如下:

可以,看出,不给ToString()方法传递IFormatProvider参数,CLR会默认采用当前线程的DateTimeFormatInfo对象实例.

注:FCL中实现IFormatProvider的接口只有三个,分别是

这些类中存在一些构造并格式化字符串时,必要的属性信息(按语言区分).

5、输出一个德国的时间字符串

var now = DateTime.Now;
//按照德文输出当前时间 g-代表公元开始时间
Console.WriteLine(now.ToString("gyyyy:MM:dd HH:mm:ss",new CultureInfo("de-DE")));

C# 自定义类型通过实现IFormattable接口,来输出指定的格式和语言文化的字符串(例:DateTime)的更多相关文章

  1. java自定义类型 比较排序 Comparator接口

    String service_time = "6:00:00,7:00:00,8:00:00,9:00:00,10:00:00,11:00:00,12:00:00,13:00:00,14:0 ...

  2. 【C# IO 操作 】IFormatProvider接口|IFormattable 接口 格式化接口

    IFormatProvider接口获取一个满足要求的个格式化器. 方法 object? GetFormat(Type? formatType);GetFormat方法主要提供一个满足指定要求的对象,该 ...

  3. Binder AIDL中自定义类型传递的源码分析

    binder机制实现的IPC和共享内存的方式不同,它采取的是值拷贝的方式,即进程间传递的实体遵循Parcelable协议, Bp端负责向Parcel里写东西,Bn端负责从Parcel里读取还原,顺序是 ...

  4. WebApi 接口返回值不困惑:返回值类型详解。IHttpActionResult、void、HttpResponseMessage、自定义类型

    首先声明,我还没有这么强大的功底,只是感觉博主写的很好,就做了一个复制,请别因为这个鄙视我,博主网址:http://www.cnblogs.com/landeanfen/p/5501487.html ...

  5. IComparable接口实现自定义类型的排序

    IComparable接口实现自定义类型的排序   CompareTo(Object) 方法的实现必须返回有三个值之一 如下表中所示. 返回值 参数比较 大于0 x>y 等于0 x=y 小于0 ...

  6. 【WCF】自定义错误处理(IErrorHandler接口的用法)

    当被调用的服务操作发生异常时,可以直接把异常的原始内容传回给客户端.在WCF中,服务器传回客户端的异常,通常会使用 FaultException,该异常由这么几个东东组成: 1.Action:在服务调 ...

  7. 《精通C#》自定义类型转化-扩展方法-匿名类型-指针类型(11.3-11.6)

    1.类型转化在C#中有很多,常用的是int类型转string等,这些都有微软给我们定义好的,我们需要的时候直接调用就是了,这是值类型中的转化,有时候我们还会需要类类型(包括结构struct)的转化,还 ...

  8. 利用IFormattable接口自动参数化Sql语句

    提要 string.Format("{0},{1}",a,b)的用法大家都不陌生了,在很多项目中都会发现很多sql语句存在这样拼接的问题,这种做法很多"懒"程序 ...

  9. [原创]java WEB学习笔记67:Struts2 学习之路-- 类型转换概述, 类型转换错误修改,如何自定义类型转换器

    本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...

随机推荐

  1. @RequestBody jackson解析复杂的传入值的一个坑;jackson解析迭代数组;jackson多重数组;jakson数组

    一.实际开发的一个问题. 传入一个json数组,数组中还嵌套数组,运用springboot+Jpa框架,@RequestBody注解传入数据 Controller @ApiOperation(valu ...

  2. java学习路线图(2018年最新版)

    最近有些网友问我如何自学 Java 后端,还有些是想从别的方向想转过来,但都不太了解 Java 后端究竟需要学什么,究竟要从哪里学起,哪些是主流的 Java 后端技术等等,导致想学,但又很迷茫,不知从 ...

  3. 第12章:MongoDB-CRUD操作--文档--查询--游标详解

    ①是什么游标 游标不是查询结果,可以理解为数据在遍历过程中的内部指针,其返回的是一个资源,或者说数据读取接口. 客户端通过对游标进行一些设置就能对查询结果进行有效地控制,如可以限制查询得到的结果数量. ...

  4. winform 可拖动无边框窗体解决办法

    方法一:通过重载消息处理实现. 鼠标的拖动只对窗体本身有效,不能在窗体上的控件区域点击拖动 /// <summary> /// 通过重载消息处理实现.重写窗口过程(WndProc),处理一 ...

  5. xslt 简单的语法

    1. 循环 <xsl:for-each select="catalog/cd"> 1 </xsl:for-each> 2. 定义变量赋值使用 <xsl ...

  6. (单调队列) Bad Hair Day -- POJ -- 3250

    http://poj.org/problem?id=3250 Bad Hair Day Time Limit: 2000MS   Memory Limit: 65536K Total Submissi ...

  7. Alpha阶段敏捷冲刺(五)

    1.站立式会议 提供当天站立式会议照片一张 2.每个人的工作 (有work item 的ID),并将其记录在码云项目管理中: 昨天已完成的工作. 祁泽文:实现了个人遗忘曲线图 徐璐琳:完成了微信Web ...

  8. android ActivityGroup接收不到onActivityResult

    android 框架嵌套,用viewgroup是很好用的.首先实现一个框架的activity,可以继承ActivityGroup 将需要切换的界面,放到ViewGroup里面. 切换如下: Inten ...

  9. Java Application和Java Applet的区别

    Java Applet和Java Application在结构方面的主要区别表现在: (1)运行方式不同.Java Applet程序不能单独运行,它必须依附于一个用HTML语言编写的网页并嵌入其中,通 ...

  10. [代码优化]PHP代码优化

    // Code snippet 1 $u_id = Hnb_Session::getInstance()->getUserID(); $arr_joinedTribeInfo = array() ...