簡體   English   中英

為什么對泛型的顯式接口調用總是調用基本實現?

[英]Why do explicit interface calls on generics always call the base implementation?

為什么在具有接口類型約束的泛型方法中的顯式C#接口調用總是調用基本實現?

例如,請考慮以下代碼:

public interface IBase
{
    string Method();
}

public interface IDerived : IBase
{
    new string Method();
}

public class Foo : IDerived
{
    string IBase.Method()
    {
        return "IBase.Method";
    }

    string IDerived.Method()
    {
        return "IDerived.Method";
    }
}

static class Program
{
    static void Main()
    {
        IDerived foo = new Foo();
        Console.WriteLine(foo.Method());
        Console.WriteLine(GenericMethod<IDerived>(foo));
    }

    private static string GenericMethod<T>(object foo) where T : class, IBase
    {
        return (foo as T).Method();
    }
}

此代碼輸出以下內容:

IDerived.Method
IBase.Method

而不是人們可能期望的:

IDerived.Method
IDerived.Method

似乎沒有辦法(缺少反射)來調用在運行時決定的類型的隱藏的,更加派生的顯式接口實現。

編輯:要清楚,如果檢查在上面的GenericMethod調用中計算結果為true:

if(typeof(T)== typeof(IDerived))

所以答案並不是因為泛型類型約束“T:class,IBase”,T總是被視為IBase。

這里的關鍵是要記住IBase.MethodIDerived.Method是兩種完全不同的方法。 我們碰巧給了他們相似的名字和簽名。 因為任何實現IDerived東西都實現了IBase ,這意味着它將有兩個名為Method ,不帶參數。 一個屬於IDerived ,一個屬於IBase

所有編譯器都知道編譯GenericMethod ,泛型參數至少會實現IBase ,因此它只能保證IBase.Method實現存在。 這就是所謂的方法。

與C ++模板不同,無論何時編譯方法都不會發生泛型替換(對於使用的每個模板參數組合,模板都會發生一次)。 相反,該方法只需編譯一次,以便在運行時可以替換任何類型。

在您的情況下,編譯器為GenericMethod發出IL,如下所示:

IL_0000:  ldarg.0     
IL_0001:  isinst      <T> 
IL_0006:  unbox.any   <T>
IL_000B:  box         <T>    
IL_0010:  callvirt    IBase.Method
IL_0015:  ret         

注意它顯式調用了IBase.Method 該方法與IDerived.Method之間沒有虛擬/覆蓋關系,因此無論在運行時中用哪種類型替換T,都可以調用基礎。

加入Kyle的答案,我在評論中無法做到,因為我還沒有足夠的聲譽......

我認為這說明了:

private static string GenericMethod<T>(T foo) where T : class, IBase
{
    return foo.Method() + " "  + typeof(T) + " " + typeof(Foo);
}

刪除對象並使參數為T,因此不需要as-cast仍然會調用IBase.Method。

我很確定這完全是由於4.4.4滿足 C#規范中的約束

在這方面,C#泛型的行為與C ++模板不同。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM