彻底学通string.Format以及IFormattable,IFormatProvider,ICustomFormatter

  自从使用.net以来就一直都在使用string.Format方法,一直没有空或者其他原因都没有深入去了解,主要还是因为项目上似乎没有这么高的要求,也没必要去深入了解,就算碰到了自定义的格式化内容也是写几个通用的方法而已。今天空下来仔细去理解了一下,在这里和大家分享一下,也希望大家一起交流。

  string.Format方法是string类提供的静态方法,一般最多使用的是其两个参数的重载,例如:

var name = "Zhezhe";

var msg = string.Format("Hello Cnblogs, I am {0},Today is {1:yyyy-MM-dd} {2}.", name, DateTime.Now, DateTime.Now.DayOfWeek);

Console.WriteLine(msg);

  后面一个参数是.net语法简写的可变参数,在.net内部实际是数组而已,实质还是两个参数的方法重载。

  你也可以不使用这种方法,将字符串相加即可:

var msg1 = "Hello Cnblogs, I am " + name + ",Today is " + DateTime.Now.ToString("yyyy-MM-dd") + " " + DateTime.Now.DayOfWeek + ".";

  上面两种方法的结果是一样的。

  之前普遍使用第一种方法的原因是相比string的多个加号相加在性能上有一定优势,因为其内部是使用StringBuilder类的,还有一个原因是代码的可读性比起+这样的方式更好一些。

  分析一下第一种方法的实现原理:

  1.Format方法的内部解析方式和原理

  Format方法在取到第一个参数"Hello Cnblogs, I am {0},Today is {1:yyyy-MM-dd} {2}."之后便将其分解成多个部分:

  ① "Hello Cnblogs, I am "   ② "{0}"  ③",Today is " ④"{1:yyyy-MM-dd}"⑤ "  " ⑥ "{2}"⑦ "."

  分解的原则是按照{}配对的数量进行的,{}是微软定义好的标记而已,你自己也可以去实现个用 []表示都无所谓。既然{}已经被定义为了特殊的标记,所以如果是自己需要在字符串中包含大括号的话就必须进行转义,这个转义也和我们平时使用的"/"转义表示法不同,需要使用两个大括号进行转义如 {{ 或者 }}。 如:

var msg2 = string.Format("Hello {{}},I am {0}", name);

  将{}分解出来之后根据中间的序号来对应第二个参数,如果第二个参数的实际个数小于需要的数量,则会出现运行错误(编译时不会报错), 如果参数个数大于序号的数量,则其后的忽略不计。

  参数个数小于序号的实际数量,错误。

var msg4 = string.Format("Hello Cnblogs, I am {0},Today is {1:yyyy-MM-dd} {2}.", name, DateTime.Now);

  参数个数大于序号的实际数量,多出的参数忽略不计。

var msg4 = string.Format("Hello Cnblogs, I am {0},Today is {1:yyyy-MM-dd}.", name, DateTime.Now,DateTime.Now.DayOfWeek);

  序号的顺序不一定必须是0,1,2,3,4可以任意排列,但是序号永远和第二个参数(实质是数组)的索引一致。

var msg4 = string.Format("Hello Cnblogs, I am {2},Today is {0:yyyy-MM-dd} {1}.", DateTime.Now, DateTime.Now.DayOfWeek, name);

  序号还能跳跃,但是中间跳跃过的序号参数里必须有。

var msg5 = string.Format("Hello Cnblogs, I am {0},Today is {2:yyyy-MM-dd} {3}.", name, "test", DateTime.Now, DateTime.Now.DayOfWeek);

  上面讲了一下用法,接下来继续。

  分解完毕之后使用 StringBuilder的Append方法将各个部分添加进去,最后再用ToString方法转成string,其实现原理非常类似于下面的代码:

var s = new StringBuilder();

            s.Append("Hello Cnblogs, I am ");

            s.Append(name);

            s.Append(",Today is ");

            s.Append(DateTime.Now.ToString("yyyy-MM-dd"));

            s.Append(" ");

            s.Append(DateTime.Now.DayOfWeek);

            s.Append(".");

            var msg3 = s.ToString();

  顺便解释一下string和StringBuilder:string虽然也是引用类型,但是该类型.net内部进行了特殊处理,让其表现出和值类型相似的特征,特别是在每次变动之后就会重新分配内存空间,而StringBuilder就不会,所以如果有很多个字符串相加拼接,则string性能较低。

  在用 Append方法进行添加的时候会有两种情况:

  一种是{0},{1}这样的不带有特殊格式化的则直接会调用该对象的ToString方法,比如上面的  s.Append(DateTime.Now.DayOfWeek);其实就是 s.Append(DateTime.Now.DayOfWeek.ToString());在.net中,如果是自己定义的类,并且没有重写ToString方法,则会输出类的全名,下面会详细讨论。

  另一种是{0:yyyy-MM-dd}带有特殊格式化的则继续分解,将冒号后面的内容分解出来,并且在调用ToString时作为参数传入,上面的s.Append(DateTime.Now.ToString("yyyy-MM-dd"));就体现了这一点。所以这些其实都没什么奥妙可言,冒号也是一个预定义好的标记而已,如果微软让你去实现这个,你也可以用其他符号。

  2.ToString方法的深入理解

  通过第一步的分析如果纯粹从分析Format这个方法来说已经足够了,大括号的特殊标记作用以及和后面参数的对应关系也已经解释清楚了。但是这里还是需要深入了解一下ToString方法。

  上面1中提到如果一个自己定义的类不去重写ToString方法的话则会 输出类的全名,例如:

public class Person

    {

        public string Name { get; set; }

    }

  如果写如下代码:

var msg6 = string.Format("Hello Cnblogs, I am {0},Today is {1:yyyy-MM-dd} {2}.",

                                     new Person() {Name = "Zhezhe"}, DateTime.Now, DateTime.Now.DayOfWeek);

            Console.WriteLine(msg6);

  则会输出:

  这里再次强调一下,如果某个对象需要转换成ToString,并且没有手动调用该方法,程序会自动调用该方法,上面的new Person() {Name = "Zhezhe"}没有手工调用,程序会自动调用方法  (new Person() {Name = "Zhezhe"}).ToString(); 这个是微软让你少些代码而已,好的习惯是始终写上 .ToString();

  .net中的任何对象都具有该方法,因为该方法在object对象中定义,任何类或者结构都会继承object,所以不用担心一个对象没有ToString方法。

  接下来定义带有ToString重载方法的类:

public class PersonWithToString

    {

        public string Name { get; set; }

        public override string ToString()

        {

            return Name;

        }

    }

  编写如下代码:

//使用自己定义类,但是重写了ToString方法

var msg7 = string.Format("Hello Cnblogs, I am {0},Today is {1:yyyy-MM-dd} {2}.", new PersonWithToString(){ Name = "Zhezhe" }, DateTime.Now, DateTime.Now.DayOfWeek);

Console.WriteLine(msg7);

  输入结果为 输出就正常了,自己重写的方法起作用了。

  总结:对自己定义的类始终重写 ToString方法。 这样在 string.Format 中或者其他需要程序自动转换成string类型时不会出现 输出类全名的情况。

继续>>下一页[第1页][第2页][第3页]

时间: 2024-08-01 00:59:03

彻底学通string.Format以及IFormattable,IFormatProvider,ICustomFormatter的相关文章

C#中string.format的格式和用法

原文:C#中string.format的格式和用法 String.Format 方法的几种定义: String.Format (String, Object) 将指定的 String 中的格式项替换为指定的 Object 实例的值的文本等效项. String.Format (String, Object[]) 将指定 String 中的格式项替换为指定数组中相应 Object 实例的值的文本等效项. String.Format (IFormatProvider, String, Object[]

《21天学通C++(第7版)》——12.2 单目运算符

12.2 单目运算符 21天学通C++(第7版) 顾名思义,单目运算符只对一个操作数进行操作.实现为全局函数或静态成员函数的单目运算符的典型定义如下: 作为类成员的单目运算符的定义如下: 12.2.1 单目运算符的类型 可重载(或重新定义)的单目运算符如表12.1所示. 表12.1 单目运算符 12.2.2 单目递增与单目递减运算符 要在类声明中编写单目前缀递增运算符(++),可采用如下语法: 而后缀递增运算符(++)的返回值不同,且有一个输入参数(但并非总是使用它): 前缀和后缀递减运算符的声

《21天学通C语言(第7版)》一2.6 课后研习

2.6 课后研习 21天学通C语言(第7版) 课后研习包含小测验和练习题.小测验帮助读者理解和巩固本课所学概念,练习题有助于读者将理论知识与实践相结合. 2.6.1 小测验 1.在C语言中,用花括号括起来的一组语句叫作什么? 2.每个C程序都必不可少的部分是什么? 3.如何在程序中添加注释?为什么要添加注释? 4.什么是函数? 5.C语言提供了哪两种类型的函数?它们有什么区别? 6.#include指令的有什么用途? 7.注释是否可以嵌套? 8.注释是否能超过一行? 9.包含文件的另一个名称是?

《21天学通Java(第7版)》—— 2.2 变量和数据类型

2.2 变量和数据类型 在第1章创建的应用程序MarsRobot中,您使用变量来跟踪信息.变量是程序运行时能够存储信息的地方.可在程序的任何地方对其中的值进行修改--因此被称为变量. 要创建变量,必须提供名称并指定它存储的信息类型.还可以在创建变量的同时给它指定初始值. 在Java中,有3种变量:实例变量.类变量和局部变量. 正如第1章中指出的,实例变量用于定义对象的属性. 类变量定义类的属性,适用于类的所有实例. 局部变量用于方法定义乃至方法中更小的语句块中.仅当Java虚拟机执行这些方法或语

《21天学通Java(第6版)》—— 2.4 字面量

2.4 字面量 21天学通Java(第6版) 除变量外,还可以在Java语句中使用字面量.字面量可以是任何直接表示一个值的数字.文本或其他信息. 下面的赋值语句使用了字面量: 其中的字面量2012表示整数值2012.数字.字符和字符串都是字面量.Java有一些特殊类型的字面量,它们表示各种数字.字符.字符串和布尔值. 2.4.1 数字字面量 Java有几种整型字面量.例如,数字4是一个int类型的整型字面量,可将其赋给byte或short类型的变量,因为它足够小,在这些整数类型的取值范围内.位于

《21天学通Java(第6版)》—— 1.4 属性和行为

1.4 属性和行为 21天学通Java(第6版)Java类包含两种不同的信息:属性和行为. 这两者在VolcanoRobot中都有,这是今天将作为类实现的项目.该项目使用计算机模拟火山探测工具,它模仿的是NASA的"遥控机器人研究"计划中用来在火山裂缝中进行研究探测的Dante II机器人. 创建该程序之前,您需要学习一些如何使用Java编写面向对象程序的知识.刚接触时,面向对象概念可能难以理解,但本书将给您提供大量将这些概念付诸实践的机会. 1.4.1 属性 属性(attribute

《21天学通Java(第6版)》—— 1.7 问与答

1.7 问与答 21天学通Java(第6版)问:实际上,方法是在类中定义的函数.既然它们无论从外观和行为方面都类似于函数,为什么不将它们叫做函数呢? 答:有些面向对象编程语言确实将它们叫做函数(C++将它们叫做成员函数).其他一些面向对象语言将位于类(对象)内.外的函数区分开来,因为在这些语言中,使用不同的术语对理解每个函数的工作原理至关重要.因为其他语言有这种区别,同时术语"方法"在面向对象技术中很常用,所以Java也使用这个术语. 问:实例变量和实例方法同类变量和类方法之间有何区别

《21天学通C语言(第6版•修订版)》一1.5 第一个C语言程序

1.5 第一个C语言程序 21天学通C语言(第6版•修订版)读者也许迫不及待地想编写第一个C语言程序.为帮助读者熟悉编译器,程序清单1.1包含一个小型程序,功能快速地完成.现在,读者也许无法理解其中的所有内容,但不用担心,尽管编写.编译并运行它. 这里的演示使用的是一个名为hello.c的程序,该程序只是将单词"Hello, World!"显示到屏幕上而已.该程序常被用来介绍C语言编程,很适合读者进行学习.程序清单1.1列出了hello.c的源代码.输入该程序清单时,请不要输入最左边的

string.Format字符串格式化说明

先举几个简单的应用案例: 1.格式化货币(跟系统的环境有关,中文系统默认格式化人民币,英文系统格式化美元) string.Format("{0:C}",0.2) 结果为:¥0.20 (英文操作系统结果:$0.20) 默认格式化小数点后面保留两位小数,如果需要保留一位或者更多,可以指定位数 string.Format("{0:C1}",23.15) 结果为:¥23.2 (截取会自动四舍五入) 格式化多个Object实例 string.Format("市场价: