[英]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.Method
和IDerived.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.