程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> C#學習之步步高(一)同名方法之間的關系

C#學習之步步高(一)同名方法之間的關系

編輯:C#入門知識

C#學習之步步高(一)同名方法之間的關系


大家好,這將是一個系列文章,都是有關於學習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#的其他特點。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved