方法多載(Overloading)是指多個名稱相同但參數個數或型別不同的方法,編譯器依傳入參數的個數、型別與順序決定使用哪一個方法。概念上多載讓方法變得更彈性,能接受不同參數組合,符合更多應用情境。舉個常見的例子,Convert.ToByte() 可傳入 int, short, string, float, double, decimal, char… 等輸入值,將其轉成 byte,傳入 string 時還能指定 16 進位(fromBase)或 IFormatProvider。

我有個根深蒂固的觀念-多載解析都發生在編譯期間,編譯器依參數將函式指標指向同名方法的其中一個。上回在談擴充方法參數傳入 dynamic 型別出錯時提到:「當 dynamic 遇上多載介面解析,一律視為 object 就對了」,但事後想想覺得怪,「多載解析都是在編譯期間完成」不適用 dynamic 型別吧?dynamic 要到執行期間才知確實型別,編譯期間要如何判斷該套用哪個多載實作?廢話不多說,做個實驗便知:有個 OverloadingMethod 共有接收 int 或 string 兩個多載版本,測試時第一次傳入數字、第二次傳入字串、第三次將數字轉型成 dynamic。

class Program
{
    static void Main(string[] args)
    {
        OverloadingMethod(12345);
        OverloadingMethod("ABCDEF");
        OverloadingMethod((dynamic)12345);
        Console.Read();
    }
 
    static void OverloadingMethod(int i)
    {
        Console.WriteLine($"int version: {i}");
    }
 
    static void OverloadingMethod(string s)
    {
        Console.WriteLine($"string version: {s}");
    }
 
}

編譯後以 ildasm 解譯回 MSIL,答案揭曉!如下圖所示,前兩次分別呼叫 int 及 string 版多載方法(黃底部分),第三次則又臭又長,透過 System.Runtime.CompilerServices、System.CSharp.RuntimeBinder 命名空間的物件與方法隔水加熱完成。

改用 LINQPad 的 IL 檢視比較簡潔,原則上類似 Reflection,第三次呼叫,方法名稱"OverloadingMethod"變成字串變數,以 Invoke 方式執行。

所以,結論是多載原則上是在編譯期間決定實際呼叫方法沒錯,但遇上 dynamic 只能留到執行期間再決定。最後,以 C# in Depth- Overloading 的這段說明為本議題劃上句點:

At compile time, the compiler works out which one it's going to call, based on the compile time types of the arguments and the target of the method call. (I'm assuming you're not using dynamic here, which complicates things somewhat.)


Comments

Be the first to post a comment

Post a comment