当前位置: 首页 > news >正文

string.Format以及IFormattable,IFormatProvider,ICustomFormatter

看了MSDN解读得头晕目炫还是不理解,百度一下找到这篇好文章:喆_喆博客《彻底学通string.Format以及IFormattable,IFormatProvider,ICustomFormatter》网址:http://kb.cnblogs.com/kb/69729/。以下来自该文章的摘抄,其中代码有自己试机。难得能解读到这样的好文章,分析得非常清楚!

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

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);
var msg1 = "Hello Cnblogs, I am " + name + ",Today is " + DateTime.Now.ToString("yyyy-MM-dd") + " " + DateTime.Now.DayOfWeek + ".";
Console.WriteLine(msg);
Console.WriteLine(msg1);

输入的两个字符串都是:
解析:

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

分解完毕:

    分解完毕之后使用 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();

在用 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"))

注意:string.Format()中参数个数小于序号的实际数量,错误;参数个数大于序号的实际数量,多出的参数忽略不计,其中序号的顺序不一定必须是0,1,2,3,4可以任意排列,且还能跳跃,但是中间跳跃过的序号参数里必须有,但是序号永远和第二个参数(实质是数组)的索引一致;如:

var msg4 = string.Format("Hello Cnblogs, I am {0},Today is {1:yyyy-MM-dd} {2}.", name, DateTime.Now);//error!
var msg4 = string.Format("Hello Cnblogs, I am {0},Today is {1:yyyy-MM-dd}.", name, DateTime.Now,DateTime.Now.DayOfWeek);
var msg5 =string.Format("Hello Cnblogs, I am {0},Today is {2:yyyy-MM-dd} {3}.", name, "test", DateTime.Now, DateTime.Now.DayOfWeek);//序号跳跃

链接知识:

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

 

2.ToString方法的深入理解

如果某个对象需要转换成ToString,并且没有手动调用该方法,程序会自动调用该方法, 这个是微软让你少些代码而已,好的习惯是始终写上 .ToString();

.net中的任何对象都具有该方法,因为该方法在object对象中定义,任何类或者结构都会继承object

ToString重载方法  

public class PersonWithToString
    {
        public string Name { get; set; }
        public override string ToString()
        {
            return Name;
        }
    }
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类型时不会出现 输出类全名的情况。

ToString带有自定义格式化参数的理解

View Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            var msg9 = string.Format("Hello Cnblogs, I am {0},Today is {1:yyyy-MM-dd} {2}.",new PersonWithToString() { Name = "Zhezhe" }.ToString("UPP"), DateTime.Now, DateTime.Now.DayOfWeek);
            var msg8 = string.Format("Hello Cnblogs, I am {0:UPP},Today is {1:yyyy-MM-dd} {2}.",
                                  new PersonWithToString() { Name = "Zhezhe" }, DateTime.Now, DateTime.Now.DayOfWeek);

            Console.WriteLine(msg9);
            Console.WriteLine(msg8);
            Console.ReadKey();
        }
    }


    public class PersonWithToString
    {
        public string Name { get; set; }

        public override string ToString()
        {
            return Name;
        }

        public string ToString(string format)
        {
            switch (format)
            {
                case "UPP":
                    return Name.ToUpper();
                case "LOW":
                    return Name.ToLower();
                default:
                    return Name;
            }
        }
    }



}

输入结果:,并不是我们所期望(两个输入的名字都大写).{0:UPP}这样的格式实际上内部处理的是和 {0}一样的效果了.原因{0:UPP} 真正调用的方法签名是 IFormattable 接口的方法string ToString(string format,IFormatProvider formatProvider),如果类没有实现IFormattable接口,那么{0:UPP} 类似这种带参的会调用重载的或者是基类的ToString()方法

小结:实现IFormattable 接口的类:

                    ①如果format参数为null(即不带格式化参数的情况,如{0})则应该调用重载的 ToString()方法,而不应该自己去另外写代码。

        ②{X}不带参数或者{X:YYYYYYY}带参的也不会去调用重载的或者是基类的ToString()方法了,它始终是去调用 接口定义的 ToString方法

        ③{X:YYYYYYY}带参达到期望的,实现该接口

        无实现IFormattable接口的类:始终调用重载的或者是基类的ToString()方法

3、继续了解 IFormatProvider 和 ICustomFormatter 接口

上面的第二点我们必须得为每个类单独去实现IFormattable接口才能实现自定义的格式化参数。在一些场后还是觉得不太方便或者说代码冗余。解决方法:.net的string.Format静态方法还提供了重载方法,具体签名如下:public static string Format(IFormatProvider provider,string format,params Object[] args)。此方法的优点是不需要为后面的参数对象实现 IFormattable  接口就可以使用自定义的格式化参数。即:

需提供了实现IFormatProvider接口的类1以及实现ICustomFormatter接口的类2.

执行过程过是: 根据new类1对象object1得到类2对象object2,然后利用object2对象的Format方法进行格式化。

使用:在string.Format()方法中传递一个类1对象即可

例子:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
           var msg15 = string.Format(new MyHelloFormatProvider(), "{0}  {1}", 
                                       new Rectangle(){ Name = "MyRectangle", Width = 14.3, Height = 10 },           new Square(){ Name = "MySquare", Side = 24.2 }); var msg16 = string.Format(new MyHelloFormatProvider(), "{0} {1}",
                                        new Rectangle(){ Name = "MyRectangle", Width = 14.3, Height = 10 }.ToString(),            new Square() { Name = "MySquare", Side = 24.2 }.ToString()); var msg17 = string.Format(new MyHelloFormatProvider(), "{0:AAA} {1:BBB}",
                                       new
Rectangle() { Name = "MyRectangle", Width = 14.3, Height = 10 }, new Square() { Name = "MySquare", Side = 24.2 });
var msg18 = string.Format(new MyHelloFormatProvider(), "{0:UPP} {1:LOW}",
                                        new Rectangle() { Name = "MyRectangle", Width = 14.3, Height = 10 }, new Square() { Name = "MySquare", Side = 24.2 }); Console.WriteLine(msg15); Console.WriteLine(msg16); Console.WriteLine(msg17); Console.WriteLine(msg18); Console.ReadKey(); } } public class Square { public string Name { get; set; } public double Side { get; set; } public override string ToString() { return string.Format("{0}(Side:{1})",Name,Side); } } public class Rectangle { public string Name { get; set; } public double Width { get; set; } public double Height { get; set; } public override string ToString() { return string.Format("{0}(Width:{1},Height:{2})",Name,Width,Height); } } public class MyHelloFormatProvider : IFormatProvider { #region IFormatProvider Members public object GetFormat(Type formateType) { return new MyHelloFormat(); } #endregion } public class MyHelloFormat : ICustomFormatter { #region ICustomFormatter Members public string Format(string format,object arg,IFormatProvider formatProvider) { var t = "hello"; switch (format) { case "UPP": t = t.ToUpper()+"_UPP"; break; case "LOW": t = t.ToLower()+"_Low"; break; default: break; } return t + arg.ToString(); } #endregion } }

前三个输出结果都是一样的:Hello MyRectangle(Width:14.3,Height:10)  Hello MySquare(Side:24.2) ,最后一行msg18带参的按期望执行。这样就不用每个类都要实现IFormattable接口才能实现自定义的格式化参数,就算以后增加了圆形,三角形等等,也都能用我们已经定义好的 MyHelloFormatProvider 和 MyHelloFormatter  去进行格式化。

疑问:如果 MyHelloFormatProvider 的 GetFormat返回的不是一个实现了 ICustomFormatter 接口的对象又会是什么情况呢?答案是会报异常。 如果返回的是 null 呢? 答案是直接调用了对象的ToString()方法了。

 

 

 

总结:1、每个类尽量重载ToString方法

         2、自定义格式化参数

                 两种方法:①实现IFormattable接口

          ②IFormatProvider 和 ICustomFormatter 接口(该方法更灵活且可解决代码冗余问题)

转载于:https://www.cnblogs.com/go-go-go/archive/2012/10/25/2738741.html

相关文章:

  • System.InvalidOperationException 异常
  • hdu 3818模拟
  • 传送门
  • 互联网创业的准备——版本控制与上线
  • C简单文件操作。。
  • CI中site_url()和base_url()的区别
  • VS2010 多线程编程
  • apex 返回标准的页面 standard view
  • 常量与变量的比较
  • Kindle 中文阅读终极优化指南
  • [置顶] 重复造轮子--IOC容器的AOP简单实现
  • 阅读作业2李嘉良篇
  • JSTL的c:forEach标签(${status.index})
  • 深入ASP.NET MVC之五:Model Binding
  • Python深入02 上下文管理器
  • [译] 理解数组在 PHP 内部的实现(给PHP开发者的PHP源码-第四部分)
  • 【跃迁之路】【519天】程序员高效学习方法论探索系列(实验阶段276-2018.07.09)...
  • 【跃迁之路】【735天】程序员高效学习方法论探索系列(实验阶段492-2019.2.25)...
  • 5分钟即可掌握的前端高效利器:JavaScript 策略模式
  • axios 和 cookie 的那些事
  • flask接收请求并推入栈
  • mysql innodb 索引使用指南
  • Protobuf3语言指南
  • Quartz实现数据同步 | 从0开始构建SpringCloud微服务(3)
  • Sass Day-01
  • SSH 免密登录
  • Unix命令
  • Webpack入门之遇到的那些坑,系列示例Demo
  • 初识MongoDB分片
  • 简析gRPC client 连接管理
  • 聊聊springcloud的EurekaClientAutoConfiguration
  • 实现简单的正则表达式引擎
  • 吐槽Javascript系列二:数组中的splice和slice方法
  • 用element的upload组件实现多图片上传和压缩
  • 用quicker-worker.js轻松跑一个大数据遍历
  • 怎样选择前端框架
  • FaaS 的简单实践
  • 湖北分布式智能数据采集方法有哪些?
  • ​configparser --- 配置文件解析器​
  • ​DB-Engines 11月数据库排名:PostgreSQL坐稳同期涨幅榜冠军宝座
  • ​ssh免密码登录设置及问题总结
  • ## 临床数据 两两比较 加显著性boxplot加显著性
  • #100天计划# 2013年9月29日
  • #define MODIFY_REG(REG, CLEARMASK, SETMASK)
  • #pragma data_seg 共享数据区(转)
  • #pragma multi_compile #pragma shader_feature
  • (4) openssl rsa/pkey(查看私钥、从私钥中提取公钥、查看公钥)
  • (9)YOLO-Pose:使用对象关键点相似性损失增强多人姿态估计的增强版YOLO
  • (C#)一个最简单的链表类
  • (delphi11最新学习资料) Object Pascal 学习笔记---第5章第5节(delphi中的指针)
  • (Matalb时序预测)WOA-BP鲸鱼算法优化BP神经网络的多维时序回归预测
  • (pytorch进阶之路)扩散概率模型
  • (初研) Sentence-embedding fine-tune notebook
  • (二)fiber的基本认识
  • (附源码)python旅游推荐系统 毕业设计 250623