1.重寫GetHashCode方法註意點: (1)重寫GetHashCode方法,也應重寫Equals方法,否者編譯器會警告。 (2)相等的對象必須有相等的散列碼(若a.Equals(b),則a.GetHashCode()==b.GetHashCode())。 (3)GetHashCode()不應引 ...
1.重寫GetHashCode方法註意點:
(1)重寫GetHashCode方法,也應重寫Equals方法,否者編譯器會警告。
(2)相等的對象必須有相等的散列碼(若a.Equals(b),則a.GetHashCode()==b.GetHashCode())。
(3)GetHashCode()不應引發任何異常,GetHashCode()必須總是成功的返回一個值。
(4)散列碼應該儘可能的保持唯一。
(5)GetHashCode()的性能應該優化,GetHashCode()通常在Equals()實現中用於“短路”一次完整的相等性比較(假如散列碼不同,當然就沒有必要進行完整的相等性比較了),所以當類型作為字典集合中的鍵類型使用時,會頻繁地調用這個方法。
(6)針對一個特定的對象,在這個對象的生存期內,GetHashCode()始終應該返回相同的值,即使對象的數據發生了改變。在許多時候,應該緩存方法的返回值,從而確保這一點。
Other:我們通常採取的做法是為來自相應類型的散列碼應用XOR(異或)運算符,並確保XOR的操作數不相近或相等,否則結果會全是零。在操作數相近或相等的情況下,考慮使用移位和加法操作。其他的備選運算符--AND和OR--具有類似的限制,這些限制會發生的更加頻繁,多次使用AND會逐漸變成全為0;而多次應用OR會逐漸變成全為1。為了進行更細緻的控制,應該使用移位運算符來分解一個比int大的類型。例如,假定有一個名為value的long類型,它的GetHashCode()方法可以像下麵這樣實現:int GetHashCode(){return (int)value ^ (int)(value >> 32)}。
2.在object中,Equals()這個virtual方法的實現是用ReferenceEquals()來評判相等性。因為這個實現往往都是不充分,所以一般都有必要重寫Equals()方法。
3.重寫Equals()方法註意點,:
(1)檢查是否為null;
(2)如果是引用類型,就檢查引用是否相等;
(3)可能要檢查散列碼是否相等,如果散列碼不相等,就沒有必要繼續執行一次全面的、逐欄位的比較。(相等的兩個對象不可能散列碼不同)
(4)比較每一個標識欄位,判斷是否相等。
4.相等性實現的指導原則:
(1)Equals()、==運算符和!=運算符應該一起實現;
(2)一個類型在Equals()、==和!=實現中應該使用相同的演算法;
(3)實現Equals()、==和!=時,也應實現一個類型的GetHashCode()方法;
(4)GetHashCode()、Equals()、==和!=永遠不能引發異常;
(5)實現IComparable時,與相等性有關的方法也應實現;
可以查看Coordinate類的定義便於直觀瞭解。
5.其他二元運算符(如“+、-、&”)的定義:就像“==”定義一樣,其中至少有一個參數的類型是本類型(當前重載運算符的類型)。在重定義了這些二元運算符後,就可以像操作基本的數值類型一樣進行運算。可查看Coordinate類。
6.其他的一元運算符(如“+正、-負、!、true、false”)的重載與重載二元運算符類似,只是重載“true、false”要成對出現,重載的參數變成了一個。其中的“true、false”運算符主要應用與if、do...while、for這些控製表達式使用。
7.轉型運算符:轉型運算符分為顯式(explicit)與隱式(implicit),隱式轉型總是成功,顯式轉型提醒用戶這是不希望的行為,顯式存在2個問題“①轉換可能會有異常,②轉換可能會存在部分數據丟失”。可查看Angle結構的代碼示例。
8.命名空間:命名空間可以嵌套,就是類一樣可以嵌套,命名空間的嵌套有2種,分別為聲明層次的嵌套;聲明時“.”符號隔開。如 System.IO。
9.生成類文件的註釋的xml文檔:可以在VS的命令工具中使用“csc /doc:文檔名.xml 類文件”。其實也可以在VS-IDE的項目屬性=>生成=>設置xml文檔輸出,即可生成項目文檔說明,當然可以使用一些免費工具進行文檔生成(如GhostDoc、NDoc)。在把程式集提供給他人使用時(程式集是不含文檔說明,編譯器會把源代碼中註釋忽略),若要使VS IntelliSense提示程式集中的成員說明信息,需讓XML文件的文件名與您要支持的程式集相同,確保XML文件與程式集位於同一個目錄中,從而在Visual Studio項目中引用程式集時,也可以找到.xml文件。
10.終結器:終結器是用來清理一個類的占用的昂貴的資源(如資料庫連接、文件句柄),其不能顯式調用,是由垃圾回收器負責調用,因此我們不能在編譯時確定終結器執行的時機,唯一確定的是終結器會在上一次使用對象之後,併在應用程式關閉之前的某個時間運行。其聲明的方式是“~類名(){}”,不允許傳遞參數與添加如public修飾符,因為其本身是不能顯式調用。基類中的終結器會作為對象終結調用的一部分而自動調用。可查看TemporaryFileStream類的處理代碼。
11.使用using語句進行確定性的終結:終結器本身的問題在於,它們不支持一個確定性終結(也就是預知一個終結器的運行時間的能力),相反,終結器是作為對資源清理的一個備用機制來使用。假如開發者忘記顯式調用必要的清理代碼,就可以依賴終結器來清理資源。要進行確定的終結需要類本身實現IDisposable介面,該介面內包含Dispose()方法,需要自己實現具體的細節來清理資源。使用using語句終結和使用try-finally處理是一個效果,因為此處的using語句在最終生成的CIL代碼上就是try-finally,using語句只是提供了try-fianlly塊的一個語法快捷方式。在using中可以實例化多個類型一致的變數,來一起處理釋放。可查看TemporaryFileStream類的處理代碼。
12.資源利用與終結的指導原則:
(1)只有在對象使用了稀缺或昂貴資源的前提下,才為對象實現finalize,終結會推遲垃圾回收。
(2)有終結器的對象應該實現IDisposable介面來支持確定性的終結。
(3)終結方法通常調用與IDisposable調用相同的代碼。
(4)終結器應避免造成任何未處理的異常。
(5)像Dispose()和Close()這樣的確定性終結方法應該調用GC.SuppressFinalize(),使垃圾回收更快的發生。
(6)資源清理方法應該足夠簡單,而且只應著重於清理由終結實例引用的資源。
(7)若基類實現了Dispose(),則派生實現應調用基類的實現。
13.延遲初始化Lazy<T>:在.net4.0中提供了Lazy<T>可對對象進行延遲初始化(即需要該對象時才被創建),可查看DataCache類的實現。
public class Coordinate { /// <summary> /// 經度 /// </summary> public Angle Longitude { get; set; } /// <summary> /// 維度 /// </summary> public Angle Latitude { get; set; } public override int GetHashCode() { int hashCode = Longitude.GetHashCode(); if (hashCode != Latitude.GetHashCode()) { hashCode ^= Latitude.GetHashCode(); } return hashCode; } public override bool Equals(object obj) { if (obj == null) { return false; } return Equals((Coordinate)obj); } public bool Equals(Coordinate obj) { if (obj == null) { return false; } if (GetHashCode() != obj.GetHashCode()) { return false; } return Longitude.Equals(obj.Longitude) && Latitude.Equals(obj.Latitude); } public static bool operator ==(Coordinate one, Coordinate two) { if (ReferenceEquals(one, null))//此處不用==判斷null,是因為我們重定義了本類的“==”操作符,否則會進入遞歸造成死迴圈。 { return ReferenceEquals(two, null); } return one.Equals(two); } public static bool operator !=(Coordinate one, Coordinate two) { return !(one == two); } public static Coordinate operator +(Coordinate one, Coordinate two) { return new Coordinate() { Longitude = one.Longitude + two.Longitude, Latitude = one.Latitude + two.Latitude }; } public static bool operator !(Coordinate one) { return false; } public static bool operator true(Coordinate one) { return one.Latitude.Hours > 0 && one.Latitude.Minutes > 0 && one.Latitude.Seconds > 0; } public static bool operator false(Coordinate one) { if (one) { return false; } return true; } } public struct Angle { public Angle(int hours, int minutes, int seconds) { Hours = hours; Minutes = minutes; Seconds = seconds; } public int Hours { get; set; } public int Minutes { get; set; } public int Seconds { get; set; } public Angle Move(int hours, int minutes, int seconds) { return new Angle(Hours + hours, Minutes + minutes, Seconds + seconds); } public override int GetHashCode() { return base.GetHashCode(); } public override bool Equals(object obj) { return Equals((Angle)obj); } public bool Equals(Angle obj) { return Hours == obj.Hours && Minutes == obj.Minutes && Seconds == obj.Seconds; } public static Angle operator +(Angle one, Angle two) { return one.Move(two.Hours, two.Minutes, two.Seconds); } public static implicit operator string(Angle one) { return string.Format("{0},{1},{2}", one.Hours, one.Minutes, one.Seconds); } public static explicit operator Angle(string text) { try { var result = text.Split(',').Cast<int>(); return new Angle(result.ElementAt(0), result.ElementAt(1), result.ElementAt(2)); } catch (Exception ex) { throw ex; } } } public class TemporaryFileStream : IDisposable { public TemporaryFileStream(string fileName) { //todo } ~TemporaryFileStream() { Dispose(); } private readonly FileStream _stream; public FileStream Stream { get { return _stream; } } private readonly FileInfo _file; public FileInfo File { get { return _file; } } public void Dispose() { Stream?.Close(); File?.Delete(); /*該語句的作用是從終結列隊中移除TemporaryFileStream類實例,一個對象在終結列隊中就是不會進行垃圾回收,必須終結後才能垃圾回收,執行此語句就不會推遲該對象的 垃圾回收。*/ GC.SuppressFinalize(this); } } public class DataCache { public DataCache() { _fileStream = new Lazy<TemporaryFileStream>(() => new TemporaryFileStream(FileStreamName)); } public string FileStreamName { get; set; } private Lazy<TemporaryFileStream> _fileStream; public TemporaryFileStream FileStream { //只有在返回“value”時,才會執行“() => new TemporaryFileStream(FileStreamName)”代碼, get { return _fileStream.Value; } } /* .net4.0以前模擬的延遲初始化對象 private TemporaryFileStream _fileStream; public TemporaryFileStream FileStream { get { if (_fileStream == null) { _fileStream = new TemporaryFileStream(FileStreamName); } return _fileStream; } }*/ }
------------------------以上內容根據《C#本質論 第三版》進行整理