首頁  >  文章  >  後端開發  >  C#學習記錄:撰寫高品質程式碼改善整理建議9-15

C#學習記錄:撰寫高品質程式碼改善整理建議9-15

php是最好的语言
php是最好的语言原創
2018-08-06 14:57:001470瀏覽

9、習慣重載運算子

在建構自己的型別時,總是應該考慮是否可以使用運算子重載

10、建立物件時需要考慮是否實作比較器

如果需要排序,有兩種比較器實作

class FirstType : IComparable<FirstType>
{
    public string name;
    public int age;
    public FirstType(int age)
    {
        name = "aa";
        this.age = age;
    }

    public int CompareTo(FirstType other)
    {
        return other.age.CompareTo(age);
    }
}

static void Main(string[] args)
{
    FirstType f1 = new FirstType(3);
    FirstType f2 = new FirstType(5);
    FirstType f3 = new FirstType(2);
    FirstType f4 = new FirstType(1);

    List<FirstType> list = new List<FirstType>
    {
        f1,f2,f3,f4
    };

    list.Sort();

    foreach (var item in list)
    {
        Console.WriteLine(item);
    }
}

C#學習記錄:撰寫高品質程式碼改善整理建議9-15

或第二種

class Program : IComparer<FirstType>
{
    static void Main(string[] args)
    {
        FirstType f1 = new FirstType(3);
        FirstType f2 = new FirstType(5);
        FirstType f3 = new FirstType(2);
        FirstType f4 = new FirstType(1);

        List<FirstType> list = new List<FirstType>
        {
            f1,f2,f3,f4
        };

        list.Sort(new Program());

        foreach (var item in list)
        {
            Console.WriteLine(item);
        }
    }

    int IComparer<FirstType>.Compare(FirstType x, FirstType y)
    {
        return x.age.CompareTo(y.age);
    }
}

它呼叫的是Program的Compare方法

11、區別對待==和Equals

無論是== 還是Equals:

對於值類型,如果類型的值相等,則返回True

對於引用類型,如果類型指向同一個對象,則返回True

且他們都可以被重載

對於string這樣一個特殊的引用類,微軟可能認為它的現實意義更傾向於一個值類型,所以在FCL(Framework Class Library)中string的比較被重載為值比較,而不是針對引用本身

從設計上來說,許多引用型別會存在類似string型別比較相近的情況,如人,他的身分證號相同,則我們就認為是一個人,這個時候就需要重載Equals方法,

一般來說,對於引用類型,我們要定義值相等的特性,應該只重寫Equals方法,同時讓==表示引用相等,這樣我們想比較哪個都是可以的

由於操作符“==”和“Equals”都可以被重載為“值相等”和“引用相等”,所以為了明確, FCL提供了object.ReferenceEquals(); 來比較兩個實例是否為同一個引用

12、重寫Equals時也要重寫GetHashCode

#字典中判斷ContainsKey的時候使用的是key類型的HashCode,所以我們想都使用類型中的某個值來作為判斷條件,就需要重新GetHashCode,當然還有其他使用HashCode來作為判斷是否相等的,如果我們不重寫,可以產生其他的效果

public override int GetHashCode()
{
    //这样写是为了减少HashCode重复的概率,至于为什么这样写我也不清楚。。 记着就行
    return (System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName +
            "#" + age).GetHashCode();
}

重寫Equals方法的同時,也應該事先一個型別安全的介面IEquatable ,所以重寫Equals的最終版本應該是

class FirstType : IEquatable<FirstType>
{
    public string name;
    public int age;
    public FirstType(int age)
    {
        name = "aa";
        this.age = age;
    }

    public override bool Equals(object obj)
    {
        return age.Equals(((FirstType)obj).age);
    }

    public bool Equals(FirstType other)
    {
        return age.Equals(other.age);
    }

    public override int GetHashCode()
    {
        return (System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName +
                "#" + age).GetHashCode();
    }

}

13、為型別輸出格式化字串

class Person : IFormattable
{
    public override string ToString()
    {
        return "Default Hello";
    }

    public string ToString(string format, IFormatProvider formatProvider)
    {

        switch (format)
        {
            case "Chinese":
                return "你好";
            case "English":
                return "Hello";
        }
        return "helo";
    }
}
static void Main(string[] args)
{
    Person p1 = new Person();
    Console.WriteLine(p1);
    Console.WriteLine(p1.ToString("Chinese",null));
    Console.WriteLine(p1.ToString("English", null));
}

C#學習記錄:撰寫高品質程式碼改善整理建議9-15

當繼承了IFormattable介面後,可以在ToString裡面穿參數來呼叫不同的ToString,上面的預設ToString就不會呼叫到了

具體還有一個IFormatProvider接口,我還沒研究

14、正確實現淺拷貝和深拷貝

無論是深拷貝還是淺拷貝,微軟都見識用型別繼承ICloneable介面的方式來明確告訴呼叫者:該型別可以被拷貝

//记得在类前添加[Serializable]的标志
[Serializable]
class Person : ICloneable
{
    public string name;

    public Child child;

    public object Clone()
    {
        //浅拷贝
        return this.MemberwiseClone();
    }

    /// <summary>
    /// 深拷贝
    /// 我也不清楚为什么这样写
    /// </summary>
    /// <returns></returns>
    public Person DeepClone()
    {
        using (Stream objectStream = new MemoryStream())
        {
            IFormatter formatter = new BinaryFormatter();
            formatter.Serialize(objectStream, this);
            objectStream.Seek(0, SeekOrigin.Begin);
            return formatter.Deserialize(objectStream) as Person;
        }
    }
}

[Serializable]
class Child
{
    public string name;
    public Child(string name)
    {
        this.name = name;
    }

    public override string ToString()
    {
        return name;
    }
}

淺拷貝:

C#學習記錄:撰寫高品質程式碼改善整理建議9-15

的p1.child.name 是更改的p2的child.name

深拷貝:

C#學習記錄:撰寫高品質程式碼改善整理建議9-15

輸出的是原來的

深拷貝是對於引用類型來說,理論上string類型是引用類型,但是由於該引用類型的特殊性,Object.MemberwiseClone仍然為其創建了副本,也就是說在淺拷貝過程中,我們應該將字串看成是值類型

15、使用dynamic來簡化反射實現

static void Main(string[] args)
{
    //使用反射
    Stopwatch watch = Stopwatch.StartNew();
    Person p1 = new Person();
    var add = typeof(Person).GetMethod("Add");
    for (int i = 0; i < 1000000; i++)
    {
        add.Invoke(p1, new object[] { 1, 2 });
    }
    Console.WriteLine(watch.ElapsedTicks);


    //使用dynamic
    watch.Reset();
    watch.Start();
    dynamic d1 = new Person();
    for (int i = 0; i < 1000000; i++)
    {
        d1.Add(1, 2);
    }
    Console.WriteLine(watch.ElapsedTicks);
}

可以看出使用dynamic會比使用反射寫出來的程式碼美觀且簡潔,而且多次運行的效率也會更高,因為dynamic第一次運行後會緩存起來

C#學習記錄:撰寫高品質程式碼改善整理建議9-15#幾乎相差了10倍

但是反射如果次數較少效率會更高

C#學習記錄:撰寫高品質程式碼改善整理建議9-15#這個是運行了100次的結果

但是很多時候效率不是必要的,始終推薦使用dynamic來簡化反射的實現

相關文章:

##C#學習記錄:撰寫高品質程式碼改善整理建議1-3#

C#學習記錄:撰寫高品質程式碼改善整理建議4-8

以上是C#學習記錄:撰寫高品質程式碼改善整理建議9-15的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn