大家好,這將是一個系列文章,都是有關於學習C#的,今天我就給大家帶來這個些列的第一章:同名方法之間的關系。
開講之前我得先簡單介紹下C#這門語言,C#是微軟於2000年6月推出的一門純粹的面向對象,是一門安全的,穩定的,簡單而優雅的由C,C++衍生出來的一門運行於.Net Framework之上的語言,其他優點還包括類型安全、組件技術、自動內存管理、跨平台異常處理、版本控制、代碼安全管理等等,其總設計師是微軟從美國Borland公司挖來的丹麥人:安德斯海爾斯伯格。
下面開始本章的內容(內容有點多,但是寫的很詳細,很值得一看)。
同名方法之間有幾種關系呢?答案是四種。
1:方法重載,方法重載是從方法體之間的參數(和方法返回值無關)來決定,對於類方法(靜態方法static method)和成員方法都適用。
2:方法重寫,方法重寫是建立在虛函數(virtual或abstract)和繼承之上,重寫函數在派生類和基類的原型是一模一樣的。
3:方法覆蓋,方法覆蓋也是建立在繼承之上,覆蓋函數在派生類和基類的原型是一模一樣的。和重寫不同的點是方法覆蓋它不是虛函數。
4:方法實現,這個比較特別,是專門針對接口而言的。
首先來來介紹一下方法重載吧:
先看下面例子:
public class SomeClass { // 方法i public void Method(int a) { Console.WriteLine("這個是方法,參數是System.Int32類型"); } // 方法ii public void Method(float a) { Console.WriteLine("這個方法,參數是System.Single類型"); } }下面是調用方:
class Program { static void Main(string[] args) { SomeClass a = new SomeClass(); // 這個調用的是方法i 代碼段[1] a.Method(3.0f); // 這個調用的方法ii 代碼段[2] a.Method(3); // ※注:方法重載是面向對象中多態的一個體現,屬於靜態連篇, // 也就是說重載的話,編譯器是找到你要調用的具體是哪個函數的 // 例如上面代碼段[1]在編譯的時候編譯器會自動幫你找到方法i並調用它 // 這種性質在方法重寫不存在,後面的例子會給大家介紹方法重寫 } }
那麼有人就會問了,如果我使用泛型或者params參數或者ref及out能進行方法重載嗎?答案是能!請看下面幾個例子:
class Program { static void Main(string[] args) { SomeClass a = new SomeClass(); // 這個調用的是方法i 代碼段[1] a.Method(3.0f); // 這個調用的方法ii 代碼段[2] a.Method(3); // ※注:方法重載是面向對象中多態的一個體現,屬於靜態連篇, // 也就是說重載的話,編譯器是找到你要調用的具體是哪個函數的 // 例如上面代碼段[1]在編譯的時候編譯器會自動幫你找到方法i並調用它 // 這種性質在方法重寫不存在,後面的例子會給大家介紹方法重寫 /*↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓這個區間之間是新加內容↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓*/ // 這個是調用的方法iii a.Method(5, 1); // 這個是調用的方法iv a.Method(4); // 這個調用的方法v var s = 0; a.Method(ref s); /*↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑*/ } } public class SomeClass { // 方法i public void Method(int a) { Console.WriteLine("這個方法,參數是System.Int32類型"); } // 方法ii public void Method(float a) { Console.WriteLine("這個方法,參數是System.Single類型"); } /*↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓這個區間之間是新加內容↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓*/ // 方法iii public void Method(int a, params int[] array) { Console.WriteLine("這個方法,參數是System.Int32和一個可選參數"); } // 方法iv public void Method (T param) { Console.WriteLine("這個方法,參數是一個泛型類型,其類型為:" + typeof(T).FullName); } // 方法v public void Method(ref int a) { Console.WriteLine("這個方法,參數是按引用傳值的System.Int32類型"); } /*↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑*/ }
那麼其實上面代碼是存在問題的,上面問題呢?看下面代碼:
class Program { static void Main(string[] args) { SomeClass a = new SomeClass(); // 這個調用的是方法i 代碼段[1] a.Method(3.0f); // 這個調用的方法ii 代碼段[2] a.Method(3); // ※注:方法重載是面向對象中多態的一個體現,屬於靜態連篇, // 也就是說重載的話,編譯器是找到你要調用的具體是哪個函數的 // 例如上面代碼段[1]在編譯的時候編譯器會自動幫你找到方法①並調用它 // 這種性質在方法重寫不存在,後面的例子會給大家介紹方法重寫 // 這個是調用的方法iii a.Method(5, 1); // 這個是調用的方法iv a.Method(4); // 這個調用的方法v var s = 0; a.Method(ref s); /*↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓這個區間之間是新加內容↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓*/ // 請看下面這個方法,其實它是符合3個方法原型的 // 它符合方法i,因為方法i:public void Method(int a)的原型參數就是一個int類型 // 其實它也符合方法iii,因為下面這個相當於在方法iii: public void Method(int a, params int[] array)中的可選參數不填 // 它也符合方法iv,因為它符合方法iv:public void Method (T param)這個原型 // 那麼問題來了,它到底調用的是方法i,方法iii,還是方法iv呢? // 答案是:方法i // 因為當重載沖突的時候,是最先考慮參數原型最簡單的,也就是方法i // 其次才是考慮可選參數方法,也就是方法iii // 最後才是考慮泛型方法,也就是方法iv a.Method(6); // 如果想顯示調用方法iii,你得這麼寫: a.Method(6, new int[0]); // 如果你想顯示調用方法iv,你得這麼寫 a.Method (6); // 好了,方法重載就先介紹到這裡了,大家先慢慢體驗吧。 /*↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑*/ } }
下面來介紹本文的第二部分內容,方法重寫。其實同名方法的這4個關系都是屬於面向對象編程中多態的一部分,方法重載和方法覆蓋都是屬於靜態聯篇的一部分,也就是說編譯器是找到你具體要掉的方法的,那麼方法重寫和實現接口就完全不一樣了,這兩者是屬於動態聯篇中的一部分的,編譯器是不知道你這個方法調用的具體是哪個的,只有等到程序運行的時候才能找到具體調用的是哪個函數。
重寫的例子,看下面把:
class Program { static void Main(string[] args) { BaseClass baseClass = new MyClass(); // 這調用的是方法iii baseClass.AbstractMethod(); // 注意,這兒調用的是方法iv而不是方法ii baseClass.VirtualMethod(); } } public abstract class BaseClass { // 方法i,這個是一個抽象函數,是一個特殊的虛函數,它沒有實現體 // 使用的關鍵字是abstract(必須) public abstract void AbstractMethod(); // 方法ii,是一個虛函數,有實現體 // 使用的關鍵字是virtual(必須) public virtual void VirtualMethod() { Console.WriteLine("來自BaseClass的方法"); } } public class MyClass : BaseClass { // 方法iii // 重寫一個方法,必須使用關鍵字(override) public override void AbstractMethod() { Console.WriteLine("重寫了基類的AbstractMethod方法"); } // 方法iv // 重寫一個方法,必須使用關鍵字(override) public override void VirtualMethod() { Console.WriteLine("重寫了基類的VirtualMethod方法"); // 這兒保留並繼續調用了基類的VirtualMethod方法,這個是一個良好的編程習慣 // 當能某些情況下這個基類的方法是不需要調用的 base.VirtualMethod(); } }
下面是程序運行結果:
下面介紹一下方法覆蓋,並且也會順便講一下方法覆蓋和方法重寫兩者的區別。
class Program { static void Main(string[] args) { /*-------------------------------------------------第一部分----------------------------------------------*/ // 注意我定義c是BaseClass類型的,但是new 的實體確實MyClass類型的 // 代碼段[1] BaseClass c = new MyClass(); // 因為這兒是一個虛方法,所以這兒的結果是調用方法iii c.Method1(); // 注意,因為它不是一虛個方法,所以它調用的不是MyClass的方法iv // 而是BaseClass的方法i,這個調用機制和虛函數不一樣 // 這種特殊的調用關系只在用基類引用接派生類實體中會出現,也就是和代碼段[1]的變量聲明方式有關 c.Method2(); /*-----------------------------------------------------------------------------------------------------------*/ /*-------------------------------------------------第二部分----------------------------------------------*/ // 我用MyClass類型的c2 new了一個MyClass類型的實體 MyClass c2 = new MyClass(); // 這兒結果是還是調用方法iii和上面第一部分一樣 // 虛函數的調用機制和變量定義類型無關 c2.Method1(); // 非虛函數的調用機制和變量定義類型有關 // 所以這兒調用的是MyClass的方法iv c2.Method2(); /*-----------------------------------------------------------------------------------------------------------*/ } } public class BaseClass { // 方法i public virtual void Method1() { Console.WriteLine("來自BaseClass的方法Method1"); } // 方法ii public void Method2() { Console.WriteLine("來自BaseClass的方法Method2"); } } public class MyClass : BaseClass { // 方法iii public override void Method1() { Console.WriteLine("重寫了來自BaseClass類的方法Method1"); base.Method1(); } // 方法iv // 注意這兒必須使用關鍵字new,否者會出現一個編譯警告 public new void Method2() { Console.WriteLine("覆蓋了基類的Method2方法"); // 同樣這兒保留並繼續調用了基類的Method2方法 // 當能某些情況下這個基類的方法是不需要調用的 base.Method2(); } }運行結果為:
最後來介紹方法實現,這個比較簡單:
public interface IMyInterface { // 方法i void Method(); // 方法ii void Method(int x); } class MyClass : IMyInterface { // 這兒實現了方法i,並把它設置為了虛方法 // 當然,去掉virtual關鍵字,把它設置為一個普通的方法也是完全可以的 public virtual void Method() { Console.WriteLine("隱式實現了方法:IMyInterface.Method"); } void IMyInterface.Method(int x) { Console.WriteLine("顯示實現了方法:IMyInterface.Method,有關接口的內容,將會在後續文章中介紹"); } }好了,現在已經23點半,我也得洗洗睡了,大家晚安。我會在後續的文章中介紹有關C#的其他特點。