2013-10-25 11 views
2

C#編譯器中發生了什麼情況,導致以下模糊調用編譯錯誤?爲什麼編譯器在傳遞繼承類型時會出現模糊的調用錯誤?

同樣的問題適用於擴展方法,或者TestClass是通用的並且使用實例而不是靜態方法。我知道它很容易解決(例如,在方法調用上投下secondInstanceTest1),但我更好奇編譯器應用什麼邏輯來選擇方法。

我的假設是,編譯器在方法檢測上應用一些特定性度量(比如CSS)來確定最具體的匹配 - 這是無效的嗎?

class Type1 { } 
class Type2 : Type1 {} 

class TestClass 
{ 
    public static void Do<T>(T something, object o) where T : Type1 
    {} 

    public static void Do(Type1 something, string o) 
    {} 
} 

void Main() 
{ 
    var firstInstance = new Type1(); 
    TestClass.Do(firstInstance, new object()); // Calls Do<T>(T, obj) 
    TestClass.Do(firstInstance, "Test"); // Calls Do(Type1, string) 

    var secondInstance = new Type2(); 
    TestClass.Do(secondInstance, new object()); // Calls Do<T>(T, obj) 
    TestClass.Do(secondInstance, "Test"); // "The call is ambiguous" compile error 
} 

//編輯:邁克ž提出我已經解釋爲被用作用於方法選擇的權重的「鑄距離」的概念。對此的測試似乎支持(儘管我不確定Type-> Generic Type是如何加權的)。

// Add the following two methods to TestClass 
public static void Do<T>(T something) where T : Type1 
{} 

public static void Do(Type1 something) 
{} 

public static void Do<T>(T something, object o) where T : Type1 
{} 

public static void Do(Type1 something, string o) 
{} 

void Main() 
{ 
    var firstInstance = new Type1(); 

    // Can't select string 
    TestClass.Do(firstInstance, new object()); // Calls Do<T>(T, obj) 

    // Do() distance is 0, Do<T> distance is 1 
    TestClass.Do(firstInstance, "Test"); // Calls Do(Type1, string) 

    // Do() distance is 0, Do<T> distance is ? (but more than 0?) 
    TestClass.Do(firstInstance); // Calls Do(Type1) 

    var secondInstance = new Type2(); 

    // Can't select string 
    TestClass.Do(secondInstance, new object()); // Calls Do<T>(T, obj) 

    // Do() distance is 1, Do<T> distance is 1 
    TestClass.Do(secondInstance, "Test"); // "The call is ambiguous" compile error 

    // Do() distance is 1, Do<T> distance is ? (but less than 1?) 
    TestClass.Do(secondInstance); // Calls Do<T>(T) 

} 
+1

它不知道選擇哪個'Do',因爲字符串也是一個對象。你可以切換你的參數順序,或者在調用時更明確'做' – Dweeberly

+0

當第一個參數顯式爲'Type1'時,它設法確定'string'比'object'更具體,但是當第一個參數繼承'Type1' –

+1

我會盡量避免這種情況。如果您沒有將通用方法命名爲非通用方法,那麼可以避免大量混淆。同樣適用於btw類。 – PMF

回答

5

重載分辨率見7.5.3節。這很複雜,但基本思想是編譯器將根據需要執行的轉換次數和類型確定「最佳」過載。

對於情況1,在泛型超載上存在精確的類型匹配。
對於情況2,在非通用過載上存在確切的類型匹配。
對於情況3,通用過載是精確匹配。注意:您的評論不正確。 T的類型將是Type2
對於情況4,通用過載需要從字符串到對象的轉換,而非通用方法需要從Type2Type1的轉換。請注意,這些都是對基本類型的引用轉換。由於在這兩種情況下都需要進行相同類型的轉換,因此編譯器拒絕爲您做出決定並給出錯誤信息,指出調用不明確。沒有「最佳」匹配。

+0

更新了不正確的評論謝謝。我認爲你是在標誌上。我不確定通用「鑄造/選擇」是如何加權的。我添加了一個更新的樣本來測試你的理論。 –

+1

@MattMitchell我不太確定我會把它稱爲「距離」,但它是這樣的。請注意,具有完全匹配參數類型的重載始終被認爲比使用泛型參數的其他等效重載更好,如後面的一些示例所示。 –

相關問題