在泛型 class 中使用 Activator.CreateInstance 并结合方法上的 "new" 修饰符
Using Activator.CreateInstance in generic class combined with "new" modifier on method
我有一个继承自 BaseClass 的 class (DerivedClass)。
在 DerivedClass 中,我在方法 (SayHello()) 上使用 "new" 修饰符,因为我想更改签名 - 我想添加一个 return 值。
我也有一个通用的 class。提供给泛型 class 的类型应该是 "BaseClass" 类型(在我的例子中是 BaseClass 或 DerivedClass)。
如果我使用 Activator.CreateInstance() 获取我的泛型类型的新实例,然后调用我的方法,则始终会调用 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();
}
}
}
因为组合:
public class GenericClass<T> where T : BaseClass
并且:
new public int SayHello()
告诉编译器,在编译时,T
将是 BaseClass
类型,并且方法重载匹配发生在编译时,而不是在 运行 时。因此,您的 运行 时间类型不同这一事实实际上并没有在这里发挥作用,因为它是 "ran over" 使用 new
修饰符,而不是通过重写虚拟方法调度,就像你的两个方法调用的 return 类型相同 (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
因为您没有重写该方法,所以您正在隐藏它。如果您使该方法成为虚拟方法,并改写它,那么您会得到您期望的结果。
GenericClass
的Run
方法不知道对象的运行时类型,它只知道它是从BaseClass
派生的类型,所以它只能绑定编译时 BaseClass
中方法的实现。由于您尚未通过制作方法 virtual
启用虚拟分派,因此它无法了解派生类型的方法。
您可以使用动态关键字:
dynamic dc2 = Activator.CreateInstance<T>();
我有一个继承自 BaseClass 的 class (DerivedClass)。
在 DerivedClass 中,我在方法 (SayHello()) 上使用 "new" 修饰符,因为我想更改签名 - 我想添加一个 return 值。
我也有一个通用的 class。提供给泛型 class 的类型应该是 "BaseClass" 类型(在我的例子中是 BaseClass 或 DerivedClass)。
如果我使用 Activator.CreateInstance
我做了一个简单的控制台应用程序来说明:
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();
}
}
}
因为组合:
public class GenericClass<T> where T : BaseClass
并且:
new public int SayHello()
告诉编译器,在编译时,T
将是 BaseClass
类型,并且方法重载匹配发生在编译时,而不是在 运行 时。因此,您的 运行 时间类型不同这一事实实际上并没有在这里发挥作用,因为它是 "ran over" 使用 new
修饰符,而不是通过重写虚拟方法调度,就像你的两个方法调用的 return 类型相同 (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
因为您没有重写该方法,所以您正在隐藏它。如果您使该方法成为虚拟方法,并改写它,那么您会得到您期望的结果。
GenericClass
的Run
方法不知道对象的运行时类型,它只知道它是从BaseClass
派生的类型,所以它只能绑定编译时 BaseClass
中方法的实现。由于您尚未通过制作方法 virtual
启用虚拟分派,因此它无法了解派生类型的方法。
您可以使用动态关键字:
dynamic dc2 = Activator.CreateInstance<T>();