• ToString 的几个思考


    1. 什么是ToString

    我们知道.NET任何类型都是派生自object类型的。而object类型有一个方法:ToString。 顾名思义,就是将一个对象实例转换为一个字符串(String)。

    image

    2. 为什么要ToString

    我们知道对象实例(instance)都是生存在内存的一个二进制字节。但如果我们需要将该对象实例显示出来(例如显示在控件中),那么就需要一个途径能够把对象实例转换为字符串。

    3. 最简单的ToString重写

    我们从上面的图片就可以看到,ToString是一个虚方法(virtual).也就代表了任何类型都可以重写该方法。不重写之前,它返回什么呢?

    class Program
    {

        static void Main(string[] args)
        {

            Customer c1 = new Customer() { CustomerName = "chenxizhang", Id = 1 };
            Console.Write(c1.ToString());

            Console.Read();

        }
    }

    class Customer
    {
        public string CustomerName { get; set; }
        public int Id { get; set; }
    }

    我们可以看到如下的结果

    image

    也就是说,默认是将类型的完整名字输出为ToString的结果。我们可以通过反射工具看一下object这个类型的代码就很明白了

    image

    但是,很显然,如果每个类型都这么输出,那么其实对用户来说一点都不友好,或者说没有任何作用。那么该如何改变这种行为呢?答案就是可以为我们的类型重写ToString方法

    class Customer
    {
        public string CustomerName { get; set; }
        public int Id { get; set; }

        public override string ToString()
        {
            return string.Format("Id={0},Name={1}", Id, CustomerName);
        }

    }

    加上这个代码之后,重新执行程序就可以看到如下的效果

    image

    看起来很酷,不是吗

    但是这样会有一个问题,就是说这样是不是只能有一个方法?也就是说只有一个可能性呢?如果我们想根据用户的一个选项来决定怎么输出,那么该怎么办呢?

    例如,用户代码可能希望只显示CustomerName,我们该怎么办?我们很自然会想到是不是再写一个重载的方法

    public string ToString(string format)
    {
        if (format == "NameOnly")
            return CustomerName;
        return ToString();
    }

    然后,在客户代码中就可以这样调用

    Console.WriteLine(c1.ToString("NameOnly"));

    这样就和谐了。

    image

    等等,事实上,是否真的要这么写呢?其实不然,.NET内置了很多所谓公用的合约(Contract),例如我们的类型要支持自定义格式化,那么就可以通过实现一个接口来完成。

    class Customer:IFormattable
    {
        public string CustomerName { get; set; }
        public int Id { get; set; }

        public override string ToString()
        {
            return string.Format("Id={0},Name={1}", Id, CustomerName);
        }

        //public string ToString(string format)
        //{
        //    if (format == "NameOnly")
        //        return CustomerName;
        //    return ToString();
        //}

        #region IFormattable 成员

        public string ToString(string format, IFormatProvider formatProvider)
        {
            if (format == "NameOnly")
                return CustomerName;

            return ToString();
        }

        #endregion
    }

    然后,可能需要稍微修改一下调用的代码

    static void Main(string[] args)
    {

        Customer c1 = new Customer() { CustomerName = "chenxizhang", Id = 1 };
        Console.WriteLine(c1.ToString());
        Console.WriteLine(c1.ToString("NameOnly",null));

        Console.Read();

    }

    经过这样修改之后,输出的结果是一样的。但使用接口的方式肯定更加规范,也更加推荐

    image

    但你可能会有一个疑问,这个ToString方法的第二个参数是什么东东呢?这就是下一节要讲的内容:定制的格式

    大家也可以再想一下,我们现在是提供了两种可能性,但如果以后又要提供其他的可能性,是不是还要回过去修改Customer这个类型呢?目前而言,是的。但这样做显然是不够灵活的。有什么办法将这个紧耦合解开吗?

    4. 定制的格式化提供程序(FormatProvider)

    我们可以通过提供者模式来解决该问题。 可以编写下面这样一个类型

    class CustomerFormatProvider : IFormatProvider,ICustomFormatter
    {

        #region IFormatProvider 成员

        public object GetFormat(Type formatType)
        {
            if (formatType == typeof(ICustomFormatter))
                return this;
            return null;
        }

        #endregion

        #region ICustomFormatter 成员

        public string Format(string format, object arg, IFormatProvider formatProvider)
        {
            Customer c = arg as Customer;
            if (arg != null)
            {
                if (format == "NameOnly")
                    return c.CustomerName;

            }

            return c.ToString();
        }

        #endregion
    }

     

    然后,我们一次性修改好Customer类型里面的那个ToString方法

    #region IFormattable 成员

    public string ToString(string format, IFormatProvider formatProvider)
    {

        ICustomFormatter formatter = formatProvider.GetFormat(typeof(ICustomFormatter)) as ICustomFormatter;

        if (formatter != null)
        {
            return formatter.Format(format, this, formatProvider);
        }

        return ToString();
    }

    #endregion

     

    然后,在客户代码中大致要这样调用

    Console.WriteLine(c1.ToString("NameOnly", new CustomerFormatProvider()));

    image

    一点都不奇怪,主程序显示的效果仍然是一样的。但是这样就是一个比较好的设计。换而言之,如果以后我们要添加一个新的格式化规则,那么我们可以单独写一个Provider,然后再调用的时候指定即可。

    5. Parse方法

    我们看到ToString方法是将对象实例转化为字符串,但是反过来,有没有什么方法是将字符串转化为对象实例的呢?这个方法就是我们所谓的Parse方法。其实我们在很多地方都看到过它的身影。例如

    image

    我们如果想为自己的类型添加Parse方法,那么重点是记住几个原则

    1. 该方法是静态的(static)

    2. 该方法的返回值类型就是类型本身

    3. 该方法的参数可以由我们随意定义,但通常都是string

    从上图中也可以看到,还有一个TryParse方法,如果从完整性方面来考虑。这是很有必要的。

  • 相关阅读:
    cpu 怎么区分指令与数据,寄存器与内存各自对应什么
    添加省略号
    有关自有属性,原型属性的问题
    实现一个new
    滚动条样式修改
    备忘录实现+具体需求应用备忘录
    Math.random生成指定范围的随机数
    reduce详细用法
    一个搜索上下的功能,用的不多
    svg拖拽rect,line,circle
  • 原文地址:https://www.cnblogs.com/chenxizhang/p/1440311.html
Copyright © 2020-2023  润新知