常规的调用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 == 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"));

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 == 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的字符串形式,输出:aConsole.WriteLine(a.ToString("G"));//返回a的枚举值,输出:1Console.WriteLine(a.ToString("D"));//返回a的十六进制表现形式,输出:00000001Console.WriteLine(a.ToString("X"));//返回a的字符串形式,输出:aConsole.WriteLine(a.ToString("F"));Console.ReadKey();}enum Type{a = 1,b = 2,c = 3}

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"));

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")));

转载于:https://www.cnblogs.com/GreenLeaves/p/9142755.html

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

  1. 自定义类型:结构体、位段、枚举、联合 ------- C语言

    C语言中,7可以存放再整型变量中,'c' 可以存放在字符型变量中,3.14可以存放在浮点型变量中,一个学生有:姓名.年龄.性别.学号等.C语言中是否有这样一个可以存放学生的类型呢?答案是肯定的,这一种 ...

  2. 【Grasshopper基础13】创建可在画布上自由传递的自定义类型数据(上)—— IGH_Goo接口的重要性及其实现

    接下来的两章,我们来介绍一下在之前章节尚未介绍到的,但却在Grasshopper中占据极其重要地位的另一批我们早就虎视眈眈但却还没想到理由要去触碰的电池们(左侧红色框指示): 是的,就是这一些带黑底的 ...

  3. hadoop自定义类型注意问题

    自定义类型要实现WritableComparable 接口,(之前只实现Writable  ,结果报错) 问题的主要原因是因为自定义类型在Partitioners 阶段要用到hashCode() 方法 ...

  4. MyBatis核心源码剖析(SqlSession XML解析 Mapper executor SQL执行过程 自定义类型处理器 缓存 日志)

    MyBatis核心源码剖析 MyBatis核心源码剖析 1 MyBatis源码概述 1.1 为什么要看MyBatis框架的源码 1.2 如何深入学习MyBatis源码 1.3 源码分析的5大原则 2 ...

  5. c++模板 --- 类模板、自定义类型当做模板参数

    生成一个类模板 类中用到了未知类型叫做类模板 用 template 修饰的类,这个类就是一个模板类 多用在数据结构中,忽略类型的问题 只要被 template 修饰,就是一个模板类,有没有用未知类型都 ...

  6. mybatis 自定义转换规则_Mybatis使用小技巧-自定义类型转换器

    一般情况下,mybatis自带的类型转换器已经足够用了,但是需求永无止境,自定义类型转换器还是学一下吧,指不定哪天会用上. 举个没啥用的示例 javabean如下: public class User ...

  7. Go 学习笔记(33)— Go 自定义类型 type(自定义结构体、结构体初始化、结构体内嵌、自定义接口)

    1. 自定义类型格式 用户自定义类型使用关键字 type ,其语法格式是: type newType oldType oldType 可以是自定义类型.预声明类型.未命名类型中的任意一种. newTy ...

  8. Go 学习笔记(35)— Go 接口 interface (接口声明、接口初始化、接口方法调用、接口运算、类型断言、类型查询、空接口)

    1. 接口概念 接口是双方约定的一种合作协议.接口实现者不需要关心接口会被怎样使用,调用者也不需要关心接口的实现细节.接口是一种类型,也是一种抽象结构,不会暴露所含数据的格式.类型及结构. 接口内部存 ...

  9. Go 学习笔记(27)— type 关键字(类型定义、类型别名、类型查询、定义接口、定义结构体)

    1. 类型别名定义 定义类型别名的写法为: type TypeAlias = Type 类型别名规定: TypeAlias 只是 Type 的别名,本质上 TypeAlias 与 Type 是同一个类 ...

最新文章

  1. android 按照星期 时间 定时_Spring Boot实现定时任务的四种方式
  2. 时间排序python_算法导论 第八章 线性时间排序(python)
  3. 通信网络基础期末复习-第三章-网络的时延分析
  4. 从零开始入门 K8s | 应用存储和持久化数据卷:核心知识
  5. python3编码命名规范_Python代码规范和命名规范
  6. 每天一道LeetCode-----在字符方格中查找某个单词
  7. SQL CE 3.0 与SQL CE 3.5区别
  8. c语言黑色星期五公式,求黑色星期五问题~
  9. 卷积神经网络CNN总结(转)
  10. 学习阮一峰老师-互联网协议笔记(IP/UDP/TCP/HTTP)
  11. 倍福--TwinSAFE的配置
  12. 激光雷达电力巡基于机载激光雷达技术的输电线路树障普查及预警
  13. PS给人物添加阴影和高光
  14. 周受资从小米跳槽字节跳动任CFO、拜腾创始人戴雷将加盟恒大汽车 | 高管变动2021年3月22日-28日...
  15. 调用百度地图api接口并点击marker也就是标点跳转页面
  16. Android补间动画之透明度动画
  17. android 10系统下载地址,Android 10正式版
  18. [284]python使用execjs执行js
  19. Ubuntu 18.04 LTS 下进入和退出tty模式
  20. 【JY|土木】失稳你过来,我们谈谈吧。

热门文章

  1. 22考研计算机专业课——数据结构【❤️考研复习指导❤️】
  2. java同步调用rabbitmq,使用RabbitMQ可以放慢同步发布/消耗速度
  3. php条件语句中大括号必须,PHP条件,括号需要?
  4. java expression 强制出现_Java中带有强制括号对的单行循环
  5. mysql在缺省状态下_MYSQL面试题
  6. android 坚挺通话广播_关于短信黑名单 BroadCast
  7. amd服务器开启虚拟化,记一次 AMD 虚拟化 IOMMU 开启过程
  8. excel表格数字显示不全_表格技巧—Excel里身份证号码显示不全的多种解决办法...
  9. int和Integer有什么区别(转)
  10. matlab图形标注名称_matlab入门(三)图像可视化