版本控制(Versioning)主要是為了解決組件的版本不兼容的問題。版本兼容的方式有:
●源代碼級兼容:依賴於舊版本的代碼在重新編譯之後能夠與新版本兼容。
●二進制兼容:依賴於舊版本的應用程序無需重新編譯就能與新版本兼容。
大多數語言根本不支持二進制級的版本兼容,其中許多在源代碼級的兼容問題上也表現的不盡如人意。事實上,許多語言由於自身的缺陷,不改寫客戶代碼就不可能實現組件的升級。
舉個例子,假設基類的作者寫了一個叫Base的類。在第一個版本中,類Base中沒有包含方法F。一個叫Derived的類從Base中繼承,並且聲明了一個方法F。類Derived和類Base一同被交付客戶使用,並且配置到許多客戶機和服務器上。
// Author A namespace A { public class Base //version 1 { } } //Author B namespace B { class Derived:A.Base { public virtual void F(){ System.Console.WriteLine("Derived.F"); } } }
到目前為止,程序的運行一切正常。然後,Base類的作者提供了一個新版本,給類Base添加了一個方法F。
//Author A namespace A { public class Base //version 2 { public virtual void F(){ //added in version 2 System.Console.WriteLine("Base.F"); } } }
新版本的Base應該和舊版本保持源代碼級兼容和二進制級兼容。不幸的是,類Base中的新方法與類Derived中的F產生了混淆。Derived應該重載Base中的F嗎?看上去不應該,因為Derived已經被編譯,那時Base中甚至還沒有F!但是,如果Derived中的F不重載Base中的F,而它又必須符合基類Base的聲明,可是在寫Derived類時還不存在該聲明。比如某種情況下,Base中的F可能要求被重載。
在解決版本問題時,C#要求開發人員清楚地表達他們的意圖。在原始代碼中,類Base不包括方法F,所以不存在什麼問題。Derived中的F是作為一個新方法,而不是重載基類中的方法。
//Author A namespace A { public class Base { } } //Author B namespace B { class Derived:A.Base { public virtual void F(){ System.Console.WriteLine("Derived.F"); } } }
一旦類Base添加了方法F並且提交了新版本,Derived的二進制版本的意圖也是很清楚的:方法F是語義獨立的,不會被作為重載來對待。但是,如果Derived被重新編譯,這就產生了混淆,編譯器將給出一個警告,並且在默認情況下,Derived中的F覆蓋了Base中的F。編譯器的警告用於提示Derived的作者主要在Base中方法F的存在。如果Derived中的F和Base中的F根本不存在什麼關系,Derived的作者可以使用new關鍵字聲明自己的意圖,從而關閉警告。
//Author A namespace A { public class Base //version 2 { public virtual void F(){ //added in version 1 System.Console.WrtieLine("Base.F"); } } } //Author B namespace B { class Derived:A.Base //version 2a:new { new public virtual void F(){ System.Console.WriteLine("Derived.F"); } } }
另一種情況是,經過對程序應用環境的調查,Derived的作者決定讓F重載Base中的方法F更為合適。這時他就應該給Derived中的F加上override關鍵字。
//Author A namespace A { public class Base //version 2 { public virtual F(){ //added in version 2 System.Console.WriteLine("Base.F"); } } } //Author B namespace B { class Derived:A.Base //version 2b:override { public override void F(){ base.F(); System.Console.WriteLine("Derived.F"); } } }
當然Derived的作者還有另外一個選擇,就是改變方法F的名字,以從根本上避免名字沖突。這種做法打破了Derived的源代碼級兼容和二進制級兼容。這就得看具體的情況了,如果Derived類不導出到其它程序,改名不失為一個好辦法,它提高了程序的可靠性,對應方法F的含義不會再出現任何混淆。