2011-03-23 145 views
2

我在看this問題,我很好奇爲什麼這deosn't編譯。擴展方法與泛型

鑑於此代碼,任何人都可以解釋爲什麼IBase.Test()的調用不解析爲正確的擴展方法?

public interface IBase { } 

public interface IChildA : IBase { } 

public interface IChildB : IBase { } 

public static class BaseExtensions 
{ 
    public static IBase Test(this IBase self) { return self; } 
    public static T Test<T>(this T self) where T : IChildB { return self; } 
} 

public static class TestClass 
{ 
    public static void Test() 
    { 
     IChildA a = null; //Yeh i know its null but just testing for compile here.. 
     IBase firstTry = a.Test(); //Cannot resolve to BaseExtensions.Test(this IBase obj) 
     IBase secondTry = ((IBase)a).Test(); //Resolves to BaseExtensions.Test(this IBase obj) 

     IChildB b = null; 
     IChildB touchedB = b.Test(); 
    } 
} 

我得到的錯誤是

Error 166 The type 'IChildA' cannot be used as type parameter 'T' in the generic type or method 'BaseExtensions.Test<T>(T)'. There is no implicit reference conversion from 'IChildA' to 'IChildB'.

我有一種感覺,那是因爲這將是ambigous對於任何實現IChildB和不知道使用哪種擴展方法,但錯誤消息不會呻吟它的一面,如果你刪除IBase firstTry = a.Test();線,那麼它編譯罰款..

+0

您的編譯器錯誤與您發佈的代碼不符(Test vs Touch)。請發佈您發佈的*精確*代碼的*確切*錯誤 - 否則我們不會知道您可能做出的其他細微更改。 – 2011-03-23 09:55:07

+0

道歉..固定 – 2011-03-23 09:56:37

+0

另請參閱http://blogs.msdn.com/b/ericlippert/archive/2009/12/10/constraints-are-not-part-of-the-signature.aspx大約一百人告訴我,我錯了,這是一個明智的規則。 – 2013-09-23 21:33:14

回答

5

好吧,問題是,在重載解析過程中,編譯器找到將適用的候選方法不含檢查方法中指定的通用約束,挑選最具體的約束,然後然後檢查通用約束。

在這種情況下,通用的方法是比非通用一個更具體(如類型參數替換後,它有效地與一個IChildA參數,而不是一個IBase參數的方法) - 但是它失敗的約束。

我有一個blog post更詳細地解釋這一點,另一個using it in a horrible way

+0

阿哈,現在有道理,我有一個跆拳道時刻:) – 2011-03-23 10:09:03

+0

我看了第二個鏈接。就一個問題;爲什麼參數定義爲'T? ignored = default(T?)'而不是'T ignored = default(T)'?我忽略了什麼,或者他們做了什麼不同的事情?我問這個問題的原因是因爲乍一看,人們可能會認爲一個可空參數是有效的, – Rob 2011-03-23 10:34:19

+1

@Rob:是的,關鍵是'T'是不可爲空的類型,而'T''是可以爲空的版本......'Nullable '上的'T:struct'約束是'T'當它是一個非結構...因爲*參數*類型(替換後)檢查約束,即使類型參數本身不是。當我在編寫惡意代碼博客文章時,「快速瀏覽」永遠不是一個好的起點:) – 2011-03-23 10:42:46