讀改善c#程式碼157個建議:建議13~15

K戰神發表於2015-07-21

目錄:

  • 建議13:為型別輸出格式化字串
  • 建議14:正確實現淺拷貝和深拷貝
  • 建議15:使用dynamic來簡化反射實現

 

一、建議13:為型別輸出格式化字串

有些型別需要我們根據業務需求提供字串的格式化輸出。

1、我們明確知道業務需求什麼樣的輸出格式,也就是型別主動格式化輸出。

可以重寫Object.ToString()方法,也可以繼承IFormattable介面實現ToString,對字串進行輸出。

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

        public string ToString(string format, IFormatProvider formatProvider)
        {
            switch (format)
            {
                case "CH": return this.ToString();
                case "US": return string.Format("{0}{1}", LastName, FirstName);
                default: return this.ToString();                
            }
        }

        public override string ToString()
        {
            return string.Format("{0}{1}", FirstName,LastName);
        }
    }

客戶端:

 Person p = new Person() { IDCode = "No1", FirstName = "Sun", LastName = "N" };

            Console.WriteLine(p);

            Console.WriteLine(p.ToString("CH", null));
            Console.WriteLine(p.ToString("US", null));

            Console.ReadKey();

例項:記得之前做過一個API展示:API的Name是英文名,還有一個欄位描述。現在我想看到API.ToString()展示的格式是:英文名:描述。就可以稍微重寫ToString()進行格式化輸出。

         public override string ToString()
        {
            if (!string.IsNullOrEmpty(this.ApiName) && !string.IsNullOrEmpty(this.Description))            
                return string.Format("{0}:{1}", this.ApiName, this.Description);
            
            return string.Empty;
        }

 

2、使用格式化器進行格式化輸出

如果類不能提供字串的格式化輸出,我們就可以使用格式化器,好處是我們可以根據需求修改格式化器的輸出。

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

格式化器:典型的格式化器應該繼承IFormatProvider(繼承這個介面有什麼作用?), ICustomFormatter

class PersonFormatter : IFormatProvider, ICustomFormatter
    {
        public object GetFormat(Type formatType)
        {
            if (formatType == typeof(ICustomFormatter))
                return this;
            else
                return null;
        }

        public string Format(string format, object arg, IFormatProvider formatProvider=null)
        {
            Person p = arg as Person;
            if (p == null)
                return string.Empty;

            switch (format)
            {
                case "CH": return string.Format("{0}{1}", p.FirstName, p.LastName);
                case "US": return string.Format("{0}{1}", p.LastName, p.FirstName);
                default: return string.Format("{0}{1}", p.FirstName, p.LastName);
            }
        }
    }

客戶端:

 Person p = new Person() { IDCode = "No1", FirstName = "Sun", LastName = "N" };

            //不重寫ToString,返回型別名稱
            Console.WriteLine(p.ToString());

            PersonFormatter pf = new PersonFormatter();

            Console.WriteLine(pf.Format("CH", p));

            Console.WriteLine(pf.Format("US", p));

            Console.WriteLine(pf.Format("", p));

            Console.ReadKey();

執行:

 

二、建議14:正確實現淺拷貝和深拷貝

建議繼承ICloneable介面。淺拷貝,拷貝的物件中的引用型別的值的改變會互相影響。而深拷貝就是為了解決淺拷貝的這個問題。

下面是繼承了ICloneable介面,並實現了淺拷貝和深拷貝的類。

這裡的深拷貝的實現方法是使用序列化格式化器:BinaryFormatter,將類成員資訊序列化成二進位制流,然後反序列化成當前類。這裡使用序列化所以類需要加上特性[Serializable],以示此類具有序列化、反序列化能力。

[Serializable]
    internal class Product:ICloneable
    {
        public string Name { get; set; }

        public int Age { get; set; }

        public NumberFlag Number { get; set; }      
        
     //淺拷貝
public object Clone() { return this.MemberwiseClone(); }      //深拷貝 public Product DeepClone() { using (System.IO.Stream ms = new System.IO.MemoryStream()) { System.Runtime.Serialization.IFormatter formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); formatter.Context = new System.Runtime.Serialization.StreamingContext(System.Runtime.Serialization.StreamingContextStates.Clone); formatter.Serialize(ms, this); ms.Position = 0; return formatter.Deserialize(ms) as Product; } } } [Serializable] internal class NumberFlag { public string Num { get; set; } }

客戶端:淺拷貝—引用型別值得改變會相互影響。

Product p1 = new Product()
            {
                Name = "1",
                Age = 1,
                Number = new NumberFlag() { Num="01"}
            };                      

            var p2 = p1.Clone() as Product;

            if (p2 != null)
            {

                p2.Number.Num = "22";

                Console.WriteLine("p1 Number:{0}.",p1.Number.Num);

                Console.WriteLine("p2 Number:{0}.",p2.Number.Num);

                Console.ReadKey();
            }

執行:

客戶端:深拷貝—不會有淺拷貝的那種情況。

Product p1 = new Product()
            {
                Name = "1",
                Age = 1,
                Number = new NumberFlag() { Num="01"}
            };                      

            var p2 = p1.DeepClone();

            if (p2 != null)
            {

                p2.Number.Num = "22";

                Console.WriteLine("p1 Number:{0}.",p1.Number.Num);

                Console.WriteLine("p2 Number:{0}.",p2.Number.Num);

                Console.ReadKey();
            }

執行:

 

 三、建議15:使用dynamic來簡化反射實現

這個建議~雖然例項中效能比反射好,但是編譯期間跳過驗證。執行時才進行型別安全檢查。待比較後,才能確定是否使用反射還是dynamic.

 

相關文章