首先我們說一下什麼是方法重載。在面對對象這樣的高級語言中都允許我們在一個類中定 義多個方法名相同、方法間參數個數和參數順序不同的方法,對於參數個數不同或者參數列 表不同的情況我們稱之為參數列表不同。需要注意的是這裡沒有提到方法的返回值。也就是 決定方法是否構成重載有兩個條件:
(1)在同一個類中;
(2)方法名相同;
(3)參數列表不同。
例如下面的代碼:
public void Show()// (1)
{
Console.WriteLine("Nothing");
}
public void Show(int number)//(2)
{
Console.WriteLine(number);
}
/*
public int Show(int number)//(3)
{
Console.WriteLine(number);
return number % 5;
}
*/
對於上面的代碼,(1)沒有參數,(2)使用了一個int類型的參 數,(1)和(2)之間就構成了重載。(2)與(3)相比僅僅返回值不同,雖然重載不關心返回值的 不同,但是在C#中不允許存在方法名和參數列表相同、返回值不同的方法,所以(2)和(3) 不能同時存在於代碼中,(3)如果不注釋掉上面的代碼是沒有辦法通過編譯的。
上面 我們僅僅討論了重載的一些基本常識,下面我們探討一下一些情況稍微復雜的重載情況。
首先我們看第一個版本:
using System;
大家猜猜這個程序的運行結果是什麼?
using System.Collections.Generic;
using System.Text;
namespace OverrideDemo
{
/// <summary>
/// 說明:本實例用於講 述重載的關系
/// 作者:周公
/// 日期:2008-09-04
/// 首發地址:http://blog.csdn.net/zhoufoxcn
/// </summary>
class Program
{
static void Main(string[] args)
{
String s = null;
Show(s);
Object o = "123";
Show(o);
}
static void Show(string s)
{
Console.WriteLine ("String");
}
static void Show(Object o)
{
Console.WriteLine("Object");
}
}
}
以下是程序運行 結果:
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。
再根據上面的結論,我們看下面的 代碼:
using System;
猜猜上面的代碼執行結果會是怎樣的?
public class Program
{ public static void Main()
{
Show(null);
Show("");
Show(1);
}
static void Show(Object o)
{
Console.WriteLine("Object");
}
static void Show (String s)
{
Console.WriteLine("String");
}
}
以下是程序運行結果:
String
Object
Object
從上面的運行結果我們可以 得出以下結論:
(1)從Show(null)最後調用的是static void Show(String s)方法我 們更進一步可以說在C#中是方法調用盡量精確匹配的。盡管null我們可以理解為一個空 object對象或者一個空字符串,但是在這裡C#還是精確為派生類。這就像我們沒有錢,可以 說沒有一分錢也可以說沒有500英鎊,但是沒有一分錢自然就沒有500億英鎊,所以我們跟別 人說沒有錢的時候沒有必要說沒有500億英鎊一樣。在這裡自然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的,也就是不可繼承的,所以我說了是假設),並且存在在上面的代碼改 變如下:
using System;
public class Program
{ public static void Main()
{
Show(null);
Show ("");
}
static void Show(Object o)
{
Console.WriteLine("Object");
}
static void Show (String s)
{
Console.WriteLine("String");
}
static void Show(A a)//假設A是String的派生類,當然實際上String類並沒有 派生類,這裡僅僅是假設
{
Console.WriteLine("A");
}
}
如果上面的假設成立,上面的代碼運行結果應該如下:
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
下面我們對第二種 情況的代碼做一些變化,代碼如下:
using System;
using System.Collections.Generic;
using System.Text;
namespace OverrideDemo
{
/// <summary>
/// 說明:本實例用於講 述重載的關系
/// 作者:周公
/// 日期:2008-09-04
/// 首發地址:http://blog.csdn.net/zhoufoxcn
/// </summary>
class Program
{
static void Main(string[] args)
{
Show(null);
Show("");
Show(1);
}
static void Show(string s)
{
Console.WriteLine("String");
}
static void Show(Object o)
{
Console.WriteLine("Object");
}
static void Show(Program p)//Program是當前方法所在的類
{
Console.WriteLine("Program");
}
}
}
上面的代碼的運行結果是什麼,你能猜出來嗎?
哈哈,上面的程序代 碼是沒有運行結果的,因為它沒有辦法編譯!編譯情況如下:
為什麼不能通過編譯呢 ?
原因就出在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類最精確,所以編譯無法通過。
附注:最近嘗試將一些比較基礎的理論的 知識用比較淺顯的話語表達出來,主要是為了方便初學者理解和學習,也許某些詞語用得不 夠professional,,但是如果詞語太professional了怕初學者理解起來有困難,敬請各位大 俠諒解,因為本文不是為你們寫的。同時也歡迎各位與我交流經驗和心得。最近垃圾郵件太 多,所以不便在這裡公開我的email,如果各位有興趣與我交流,請加我為csdn好友,這樣就 能看見我的QQ和email了.