首页 > 学院 > 开发设计 > 正文

编写高质量代码改善C#程序的157个建议[为类型输出格式化字符串、实现浅拷贝和深拷贝、用dynamic来优化反射]

2019-11-17 03:15:30
字体:
来源:转载
供稿:网友

编写高质量代码改善C#程序的157个建议[为类型输出格式化字符串、实现浅拷贝和深拷贝、用dynamic来优化反射]

前言

  本文已更新至http://www.VEVb.com/aehyok/p/3624579.html。本文主要学习记录以下内容:

  建议13、为类型输出格式化字符串

  建议14、正确实现浅拷贝和深拷贝

  建议15、使用dynamic来简化反射实现

建议13、为类型输出格式化字符串

  有两种方法可以为类型提供格式化的字符串输出。

  一种是意识到类型会产生格式化字符串输出,于是让类型继承接口IFormattable。这对类型来说,是一种主动实现的方式,要求开发者可以预见类型在格式化方面的要求。

  更多的时候,类型的使用者需为类型自定义格式化器,这就是第二种方法,也是最灵活多变的方法,可以根据需求的变化为类型提供多个格式化器。

  下面我们就来看一下这两种方式的实现。

  最简单的字符串输出是为类型重写ToString()方法,如果没有为类型重写该方法,默认会调用Ojbect的ToString方法,它会返回当前类型的类型名称。但即使是重写了ToString()方法,提供的字符串输出也是非常单一的,而通过实现IFormattable接口的ToString()方法,可以让类型根据用户的输入而格式化输出。

下面我们来看一个简单的小例子:

    public class Person:IFormattable    {        public string IDCode { get; set; }        public string FirstName { get; set; }        public string LastName { get; set; }        /// <summary>        /// 实现接口Iformattable的方法ToString        /// </summary>        /// <param name="format"></param>        /// <param name="formatPRovider"></param>        /// <returns></returns>        public string ToString(string format, IFormatProvider formatProvider)        {            switch (format)            {                 case"Ch":                    return this.ToString();                case"Eg":                    return string.Format("{0}{1}", this.FirstName, this.LastName);                default:                    return                        this.ToString();            }        }        ///重写Object的方法ToString()        public override string ToString()        {            return string.Format("{0}{1}",this.LastName,this.FirstName);        }    }

调用代码如下所示:

        static void Main(string[] args)        {            Person person = new Person() { FirstName="Kris",LastName="aehyok"};            Console.WriteLine(person);            Console.WriteLine(person.ToString("Ch",null));            Console.WriteLine(person.ToString("Eg", null));            Console.ReadLine();        }

调用执行结果如下:

  下面我们来继续介绍第二实现方式——格式化器。如果类型本身没有提供格式化的功能,那么格式化器就可以派上用场了。格式化器的好处就是可以根据需求的变化,随时增加或者修改它。

  接下来我们继续来看另外的一个小例子:

首先定义一个实体类Person:

    public class Person    {        public string IDCode { get; set; }        public string FirstName { get; set; }        public string LastName { get; set; }    }

一个典型的格式化器应该继承IFormatProvider和ICustomerFormatter,看代码:

    public class PersonFomatter:IFormatProvider,ICustomFormatter    {        #region IFormatProvider成员        public object GetFormat(Type formatType)        {            if (formatType == typeof(ICustomFormatter))            {                return this;            }            else            {                return null;            }        }        #endregion        #region ICustomFormatter成员        public string Format(string format, object arg, IFormatProvider formatProvider)        {            Person person = arg as Person;            if (person == null)            {                return string.Empty;            }            switch (format)            {                 case"Ch":                    return string.Format("{0} {1}",person.LastName,person.FirstName);                case"":                    return string.Format("{0} {1}",person.FirstName,person.LastName);                case"CHM":                    return string.Format("{0} {1}:{2}", person.LastName, person.FirstName, person.IDCode);                default:                    return string.Format("{0} {1}", person.LastName, person.FirstName);            }        }        #endregion    }

调用代码如下:

    class Program    {        static void Main(string[] args)        {            Person person = new Person() { FirstName="Kris", LastName="aehyok", IDCode="ID000001"};            Console.WriteLine(person.ToString());            PersonFomatter pFomatter = new PersonFomatter();            Console.WriteLine(pFomatter.Format("Ch", person, null));            Console.WriteLine(pFomatter.Format("Eg", person, null));            Console.WriteLine(pFomatter.Format("CHM", person, null));            Console.ReadLine();        }    }

调用执行结果如下:

其实还有另外一种变通的形式,就是将这两种方式合并一起使用的过程,下面来看一下具体的实现代码:

public class Person:IFormattable    {        public string IDCode { get; set; }        public string FirstName { get; set; }        public string LastName { get; set; }                /// <summary>        /// 实现接口Iformattable的方法ToString        /// </summary>        /// <param name="format"></param>        /// <param name="formatProvider"></param>        /// <returns></returns>        public string ToString(string format, IFormatProvider formatProvider)        {            switch (format)            {                 case"Ch":                    return this.ToString();                case"Eg":                    return string.Format("{0}{1}", this.FirstName, this.LastName);                default:                    //return this.ToString();                    ICustomFormatter customerFormatter = formatProvider as ICustomFormatter;                    if (formatProvider == null)                    {                        return this.ToString();                    }                    return customerFormatter.Format(format, this, null);            }        }        ///重写Object的方法ToString()        public override string ToString()        {            return string.Format("{0}{1}",this.LastName,this.FirstName);        }    }

PersonFomatter自定义格式化器的代码并没有发生任何的改变。调用代码如下:

        static void Main(string[] args)        {            Person person = new Person() { FirstName="Kris", LastName="aehyok", IDCode="ID000001"};            Console.WriteLine(person.ToString());            PersonFomatter pFomatter = new PersonFomatter();            Console.WriteLine(pFomatter.Format("Ch", person, null));            Console.WriteLine(pFomatter.Format("Eg", person, null));            Console.WriteLine(pFomatter.Format("CHM", person, null));            Console.WriteLine(person.ToString("Ch",pFomatter));            Console.WriteLine(person.ToString("Eg", pFomatter));            Console.WriteLine(person.ToString("CHM", pFomatter));            Console.ReadLine();        }

调用执行结果如下所示:

建议14、正确实现浅拷贝和深拷贝

为对象创建副本的技术成为拷贝(也叫克隆)。我们将拷贝分为浅拷贝和深拷贝。

浅拷贝 将对象中的所有字段复制到新的对象(副本)中。其中,值类型字段的值被复制到副本中后,在副本中的修改不会影响到源对象对应的值。而引用类型的字段被复制到副本中的是引用类型的引用,而不是引用的对象,在副本中对引用类型的字段值做修改会影响到源对象本身。

深拷贝同样,将对象中的所有字段复制到新的对象中。不过无论是对象的值类型字段,还是引用类型字段,都会被重新创建并赋值,对于副本的修改,不会影响到源对象本身。

无论是浅拷贝还是深拷贝,微软都建议用类型继承ICloneable接口的方式明确告诉调用者:该类型可以被拷贝。当然,ICloneable接口只提供了一个声明为Clone的方法,我们可根据需求在Clone方法内实现浅拷贝或深拷贝。一个简答的浅拷贝的实现代码如下所示:

首先定义实体类:

    public class Employee:ICloneable    {        public string IDCode { get; set; }        public int Age { get; set; }        public Department Department { get; set; }        #region OCloneable成员        public object Clone()        {            return this.MemberwiseClone();        }        #endregion    }    public class Department    {        public string Name{get;set;}        public override string  ToString()        {              return this.Name;        }    }

然后进行调用代码如下:

        static void Main(string[] args)        {            Employee Niki = new Employee()            {                IDCode = "IDaehyok",                Age = 25,                Department = new Department() { Name="Depart1" }            };            Employee Kris = Niki.Clone() as Employee;            Console.WriteLine(string.Format("IDCode:{0}/tAge:{1}/tDepartment:{2}", Kris.IDCode, Kris.Age, Kris.Department));            ///开始改变Niki的值
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表