我有一个继承自BaseClass的类(DerivedClass)。
在DerivedClass中,我使用" new"一个方法的修饰符(SayHello()),因为我想改变签名 - 我想添加一个返回值。
我也有一个通用类。提供给泛型类的类型应为" BaseClass" (在我的例子中是BaseClass或DerivedClass)。
如果我使用Activator.CreateInstance< T>()来获取我的泛型类型的新实例,然后调用我的方法,那么总是调用BaseClass上的方法。为什么DerivedClass上的SayHello方法在被设置为泛型类型时会被调用?
我制作了一个简单的控制台应用程序来说明:
namespace TestApp
{
using System;
class Program
{
static void Main(string[] args)
{
var gc = new GenericClass<DerivedClass>();
gc.Run();
}
}
public class BaseClass
{
public void SayHello()
{
Console.WriteLine("Hello!");
}
}
public class DerivedClass : BaseClass
{
new public int SayHello()
{
Console.WriteLine("Hello returning int!");
return 1;
}
}
public class GenericClass<T> where T : BaseClass
{
public void Run()
{
var bc = new BaseClass();
bc.SayHello(); // Hello!
var dc = new DerivedClass();
dc.SayHello(); // Hello returning int!
var dc2 = Activator.CreateInstance<T>();
dc2.SayHello(); // Hello!
Console.WriteLine(dc2.GetType()); // TestApp.DerivedClass
Console.Read();
}
}
}
答案 0 :(得分:2)
因为您没有覆盖该方法,所以您要将其隐藏起来。如果您将方法设为虚拟,并改为覆盖它,那么您将获得您期望的结果。
Run
的{{1}}方法不知道对象的运行时类型,它只知道它是从GenericClass
派生的类型,所以它只能在编译时绑定BaseClass
中方法的实现。由于您尚未通过制作方法BaseClass
启用虚拟调度,因此无法了解派生类型的方法。
答案 1 :(得分:1)
因为组合:
public class GenericClass<T> where T : BaseClass
和
new public int SayHello()
告诉编译器,在编译时,T
的类型为BaseClass
,并且方法重载匹配发生在编译时,而不是在运行时。因此,您的运行时类型不同的事实在这里实际上并没有发挥作用,因为它是使用new
修饰符“跑过来”,而不是通过覆盖虚拟方法调度,因为它会如果两个方法调用的返回类型相同(void
)。
您在生成的IL中看到它:
<强> GenericClass`1.Run:强>
IL_001B: call 01 00 00 2B
IL_0020: stloc.2 // dc2
IL_0021: ldloca.s 02 // dc2
IL_0023: constrained. 02 00 00 1B
IL_0029: callvirt UserQuery+BaseClass.SayHello
答案 2 :(得分:0)
您可以使用动态关键字:
dynamic dc2 = Activator.CreateInstance<T>();