9、習慣重載運算子
在建構自己的型別時,總是應該考慮是否可以使用運算子重載
如果需要排序,有兩種比較器實作
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); } }
或第二種
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方法
無論是== 還是Equals:
對於值類型,如果類型的值相等,則返回True
對於引用類型,如果類型指向同一個對象,則返回True
且他們都可以被重載
對於string這樣一個特殊的引用類,微軟可能認為它的現實意義更傾向於一個值類型,所以在FCL(Framework Class Library)中string的比較被重載為值比較,而不是針對引用本身
從設計上來說,許多引用型別會存在類似string型別比較相近的情況,如人,他的身分證號相同,則我們就認為是一個人,這個時候就需要重載Equals方法,
一般來說,對於引用類型,我們要定義值相等的特性,應該只重寫Equals方法,同時讓==表示引用相等,這樣我們想比較哪個都是可以的
由於操作符“==”和“Equals”都可以被重載為“值相等”和“引用相等”,所以為了明確, FCL提供了object.ReferenceEquals(); 來比較兩個實例是否為同一個引用
#字典中判斷ContainsKey的時候使用的是key類型的HashCode,所以我們想都使用類型中的某個值來作為判斷條件,就需要重新GetHashCode,當然還有其他使用HashCode來作為判斷是否相等的,如果我們不重寫,可以產生其他的效果
public override int GetHashCode() { //这样写是为了减少HashCode重复的概率,至于为什么这样写我也不清楚。。 记着就行 return (System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName + "#" + age).GetHashCode(); }
重寫Equals方法的同時,也應該事先一個型別安全的介面IEquatable
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(); } }
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)); }
當繼承了IFormattable介面後,可以在ToString裡面穿參數來呼叫不同的ToString,上面的預設ToString就不會呼叫到了
具體還有一個IFormatProvider接口,我還沒研究
無論是深拷貝還是淺拷貝,微軟都見識用型別繼承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; } }
淺拷貝:
的p1.child.name 是更改的p2的child.name
深拷貝:
輸出的是原來的
深拷貝是對於引用類型來說,理論上string類型是引用類型,但是由於該引用類型的特殊性,Object.MemberwiseClone仍然為其創建了副本,也就是說在淺拷貝過程中,我們應該將字串看成是值類型
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第一次運行後會緩存起來
#幾乎相差了10倍
但是反射如果次數較少效率會更高
#這個是運行了100次的結果
但是很多時候效率不是必要的,始終推薦使用dynamic來簡化反射的實現
相關文章:
以上是C#學習記錄:撰寫高品質程式碼改善整理建議9-15的詳細內容。更多資訊請關注PHP中文網其他相關文章!