把C#中方法重載說透

來源:互聯網
上載者:User

    首先我們說一下什麼是方法重載。在面對對象這樣的進階語言中都允許我們在一個類中定義多個方法名相同、方法間參數個數和參數順序不同的方法,對於參數個數不同或者參數列表不同的情況我們稱之為參數列表不同。需要注意的是這裡沒有提到方法的傳回值。也就是決定方法是否構成重載有三個條件:
(1)在同一個類中;
(2)方法名相同;
(3)參數列表不同。

例如下面的代碼:

  1.     public void Show()//(1)
  2.         {
  3.             Console.WriteLine("Nothing");
  4.         }
  5.         public void Show(int number)//(2)
  6.         {
  7.             Console.WriteLine(number);
  8.         }
  9.         /*
  10.         public int Show(int number)//(3)
  11.         {
  12.             Console.WriteLine(number);
  13.             return number % 5;
  14.         }
  15.         */

 對於上面的代碼,(1)沒有參數,(2)使用了一個int類型的參數,(1)和(2)之間就構成了重載。(2)與(3)相比僅僅傳回值不同,雖然重載不關心傳回值的不同,但是在C#中不允許存在方法名和參數列表相同、傳回值不同的方法,所以(2)和(3)不能同時存在於代碼中,(3)如果不注釋掉上面的代碼是沒有辦法通過編譯的。

上面我們僅僅討論了重載的一些基本常識,下面我們探討一下一些情況稍微複雜的重載情況。

首先我們看第一個版本:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. namespace OverrideDemo
  5. {
  6.     /// <summary>
  7.     /// 說明:本執行個體用於講述重載的關係
  8.     /// 作者:周公
  9.     /// 日期:2008-09-04
  10.     /// 首發地址:http://blog.csdn.net/zhoufoxcn
  11.     /// </summary>
  12.     class Program
  13.     {
  14.         static void Main(string[] args)
  15.         {
  16.             String s = null;
  17.             Show(s);
  18.             Object o = "123";
  19.             Show(o);
  20.         }
  21.         static void Show(string s)
  22.         {
  23.             Console.WriteLine("String");
  24.         }
  25.         static void Show(Object o)
  26.         {
  27.             Console.WriteLine("Object");
  28.         }
  29.     }
  30. }

大家猜猜這個程式的運行結果是什嗎?
以下是程式運行結果:
String
Object

對以上代碼進行分析,我們發現Show()方法有兩種形式,一種是string類型的參數,一種是object型別參數,在一個類中存在方法名相同、參數列表不同(參數個數或者參數類型不同)的現象我們稱之為overloading,即重載。不過這裡的Show()方法的參數比較特殊,因為string類繼承於Object類,也就是Show()方法的參數存在一種繼承關係。從結果我們可以得出兩點結論:
(1)從String s = null;Show(s);最後調用的是static void Show(string s)這個方法我們可以得出,C#中方法調用是精確匹配的,也就是s是string類型,雖然string類型繼承自object類型,儘管static void Show(Object o)也滿足條件,但是方法聲明中static void Show(string s)這個聲明與s類型的最接近(因為s是string類型,與它最接近),所以執行static void Show(string s),而不執行static void Show(Object o)這個方法。
(2)從Object o = "123"; Show(o);最後調用的是static void Show(Object o)這個方法我們可以得出,C#中如果存在方法重載,會根據其refrence type(參考型別)來調用對象的方法,而不是根據instance type(執行個體類型)來調用。儘管”123”是string類型,但是它的refrence type是object類型的,所以會調用static void Show(Object o)這個方法而不是static void Show(string s)。
上面的Main()方法的IL代碼如下:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // 代碼大小       24 (0x18)
  .maxstack  1
  .locals init ([0] string s,
           [1] object o)
  IL_0000:  nop
  IL_0001:  ldnull
  IL_0002:  stloc.0
  IL_0003:  ldloc.0
  IL_0004:  call       void OverrideDemo.Program::Show(string)
  IL_0009:  nop
  IL_000a:  ldstr      "123"
  IL_000f:  stloc.1
  IL_0010:  ldloc.1
  IL_0011:  call       void OverrideDemo.Program::Show(object)
  IL_0016:  nop
  IL_0017:  ret
} // end of method Program::Main

從上面的IL代碼我們可以看出對於string s=null;這句代碼在IL中表示為:ldnull。

再根據上面的結論,我們看下面的代碼:

  1. using System;
  2. public class Program
  3. {   public static void Main()
  4.     {
  5.     Show(null);
  6.     Show("");
  7.     Show(1);
  8.     }
  9.     static void Show(Object o)
  10.     {
  11.     Console.WriteLine("Object");
  12.     }
  13.     
  14.     static void Show(String s)
  15.     {    Console.WriteLine("String"); 
  16.     }
  17. }

     

    猜猜上面的代碼執行結果會是怎樣的?
    以下是程式運行結果:
    String
    Object
    Object

    從上面的運行結果我們可以得出以下結論:
    (1)從Show(null)最後調用的是static void Show(String s)方法我們更進一步可以說在C#中是方法調用盡量精確匹配的。儘管null我們可以理解為一個空object對象或者一個Null 字元串,但是在這裡C#還是精確為衍生類別。這就像我們沒有錢,可以說沒有一分錢也可以說沒有500英鎊,但是沒有一分錢自然就沒有500億英鎊,所以我們跟別人說沒有錢的時候沒有必要說沒有500億英鎊一樣。在這裡自然null就表示Null 字元串。所以Show(null)這個方法會調用static void Show(String s)這個方法。
    這有點像下面的情況:
    一次活動大會上,主持人說:“身高不到1.60m的請坐在1到3排,身高不到1.75m的請做到4到6排,其他的請隨便坐。”
    上面的語句似乎有些邏輯方面的問題,應該說身高超過1.60m但是不到1.75m的請坐到4到6排。但是如果你面對著一群拿著槍的強盜,他說上面的話時,恰好你也在場並且你的身高是1.55m,你會坐到哪一排?你總不可能冒著挨一槍的危險去糾正他的邏輯錯誤吧?最好的辦法是坐到1到3排。因為無論怎麼說你的身高是絕對滿足不到1.60m這個條件的(儘管你的身高也滿足強盜說的第二個條件,即身高不到1.75米,但是你肯定不會冒這個危險,從上下句的意思我們也能推斷出人家的意思就是身高在1.61m到1.74m之間的人坐4到6排)。
    在上面的代碼中,你在運行環境的眼中就是一個持槍的強盜,雖然null可以理解為null類型的string或者null類型的object,但是它不能向你問清楚這個到底是null類型的string或者null類型的object,因為string是Object的衍生類別,所以它按照null類型的string來調用相應的方法了。
    (2)從Show("")最後調用static void Show(String s)這個方法進一步證明了方法調用是盡量選擇參數最匹配的那個執行。因為Show("")相當於:string s = ""; Show(s);s的參考型別是string,所以會調用static void Show(String s)這個方法。
    我們在這裡可以假設一下:假如存在一個類A是String類的衍生類別(實際上string類是sealed的,也就是不可繼承的,所以我說了是假設),並且存在在上面的代碼改變如下:

    1. using System;
    2. public class Program
    3. {   public static void Main()
    4.     {
    5.     Show(null);
    6.     Show("");
    7.     }
    8.     static void Show(Object o)
    9.     {
    10.     Console.WriteLine("Object");
    11.     }
    12.     
    13.     static void Show(String s)
    14.     {
    15.     Console.WriteLine("String");
    16.     }
    17.     static void Show(A a)//假設A是String的衍生類別,當然實際上String類並沒有衍生類別,這裡僅僅是假設
    18.     {
    19.     Console.WriteLine("A");
    20.     }
    21.     
    22. }

    如果上面的假設成立,上面的代碼運行結果應該如下:
    A
    String

    (3)為什麼Show(1)會調用static void Show(Object o)這個方法呢?在這個類中與Show(1)最精確的方法重載應該是static void Show(int i)這種方法聲明,但是方法中沒有,因為int是繼承自ValueType類,所以如果沒有static void Show(int i)這種聲明,那麼其次接近的聲明應該是static void Show(ValueType v)這種聲明,可惜方法中依然沒有,不過ValueType類繼承自Object類,所以比static void Show(ValueType v)還次一點的方法重載聲明應該是static void Show(Object o),而類中也確實存在這種聲明,所以會調用static void Show(Object o)這個方法。當然從int到Object這個過程中存在一次box,也就是裝箱(裝箱是從實值型別到參考型別的轉換),這個可以從下面的IL代碼可以看出來。

    以下是第二種情況下Main()方法的IL代碼:
    .method private hidebysig static void  Main(string[] args) cil managed
    {
      .entrypoint
      // 代碼大小       32 (0x20)
      .maxstack  8
      IL_0000:  nop
      IL_0001:  ldnull
      IL_0002:  call       void OverrideDemo.Program::Show(string)
      IL_0007:  nop
      IL_0008:  ldstr      ""
      IL_000d:  call       void OverrideDemo.Program::Show(string)
      IL_0012:  nop
      IL_0013:  ldc.i4.1
      IL_0014:  box        [mscorlib]System.Int32
      IL_0019:  call       void OverrideDemo.Program::Show(object)
      IL_001e:  nop
      IL_001f:  ret
    } // end of method Program::Main

    下面我們對第二種情況的代碼做一些變化,代碼如下:

    1. using System;
    2. using System.Collections.Generic;
    3. using System.Text;
    4. namespace OverrideDemo
    5. {
    6.     /// <summary>
    7.     /// 說明:本執行個體用於講述重載的關係
    8.     /// 作者:周公
    9.     /// 日期:2008-09-04
    10.     /// 首發地址:http://blog.csdn.net/zhoufoxcn
    11.     /// </summary>
    12.     class Program
    13.     {
    14.         static void Main(string[] args)
    15.         {
    16.             Show(null);
    17.             Show(""); 
    18.             Show(1);
    19.         }
    20.         static void Show(string s)
    21.         {
    22.             Console.WriteLine("String");
    23.         }
    24.         static void Show(Object o)
    25.         {
    26.             Console.WriteLine("Object");
    27.         }
    28.         static void Show(Program p)//Program是當前方法所在的類
    29.         {
    30.             Console.WriteLine("Program");
    31.         }
    32.     }
    33. }

     

    上面的代碼的運行結果是什麼,你能猜出來嗎?
    哈哈,上面的程式碼是沒有運行結果的,因為它沒有辦法編譯!編譯情況如下:
     
    為什麼不能通過編譯呢?
    原因就出在Show(null)這個方法這裡!如果僅僅有static void Show(string s)和static void Show(Object o)方法構成重載關係,那麼null我們既可以理解為空白string引用也可以理解為空白Object引用,因為string類型的限制更精確一些,所以C#會按照最精確地匹配成string類型,因而會執行static void Show(string s)這個方法。這是在前面的代碼中已經被證明的。可是現在多了一個static void Show(Program p)方法的重載,null既可以理解成空string類型引用,也可以理解成空Program類型引用,因為string類和Program類都是Object類的衍生類別,所以按照前面的推論自然不會當成空Object類型的引用。因為String類和Program類之間不存在繼承關係,按照最精確匹配原則,編譯器無法決定匹配成String類還是Program類最精確,所以編譯無法通過。

    相關文章

    聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.