我的问题类似于使用List
class Parent { }
class Child1 : Parent { }
class Child2 : Parent { }
// could be other child classes, unknown to the Process class
class Process
{
public Process()
{
List<Parent> L = new List<Parent>();
Child1 C1 = new Child1();
Child2 C2 = new Child2();
L.Add(C1);
L.Add(C2);
foreach(Parent P in L)
{
DoSomething(P);
}
}
public void DoSomething<TypeP>(TypeP P)
{
// P is 'Child1', 'Child2' or potentially something else
// TypeP is 'Parent'
//TypeP = P.GetType(); //doesnt work
//DoSomethingElse<P.GetType()>(); //doesnt work
DoSomethingElse<TypeP>();
}
public void DoSomethingElse<T>()
{
// T is 'Parent'
T R = Activator.CreateInstance<T>(); // want R to be 'Child1' or 'Child2'
}
}
我希望用 C1 或 C2 的类型而不是"父"类型来调用 DoSomethingElse。我已经来过几次了,但找不到让它工作的方法。我觉得它应该是微不足道的,因为 DoSomething 中的 P 是正确的类型。
编辑:DoSomethingElse 是泛型的原因是,可能从父类派生任意数量的子类。我以 Child1 和 Child2 为例,但实际上可能还有更多,因此 DoSomethingElse 中的 switch 语句是不可行的,因为派生类的数量是未知的。
代码有几个问题。泛型仅适用于编译时已知的类型。我们将所有值转换为基类:
// L is a List<Parent>
L.Add(C1);
L.Add(C2);
因此,如果我们DoSomething<TypeP>
使用泛型,编译器将始终使用 Parent
进行TypeP
.我们需要使用仅在运行时已知的类型,我们的选择是有限的。称为 DLR 的东西用于处理运行时值。DLR 使用的类型称为 dynamic
。
因此,让我们将foreach
迭代变量更改为dynamic
:
foreach (dynamic P in L)
{
DoSomething(P);
}
现在我们可以使用带有约束的泛型,即TChild
应该从Parent
派生:
public void DoSomething<TChild>(TChild P) where TChild : Parent
{
...
DoSomethingElse(P);
}
我们不能使用此方法签名,因为它需要我们在方法调用时指定类型:
public void DoSomethingElse<TChild>()
为了让编译器推断类型,我们可以传递编译器可以用来推断类型的TChild
实例:
public void DoSomethingElse<TChild>(TChild P) where TChild : Parent
{
// TChild is Child now
var R = Activator.CreateInstance(typeof(TChild)); // want R to be 'Child1' or 'Child2'
}
这是DotNetFiddle的例子
这里有两个选项:
-
将 DoSomethingElse 替换为 Parent 类中的虚拟方法,因此任何子级都必须覆盖您需要的行为。然后,通常只需通过父类型实例(Liskov 替换原则(调用该重写方法。首选方式,恕我直言。
public void DoSomething<TypeP>(TypeP P) { P.DoSomethingElse(); // this is virtual method to be overwritten by children types }
-
"泛型专业化"方法(这对我来说似乎有点笨拙,因为 C# 自然缺乏一些C++功能,但是......您可能会发现它合适(https://stackoverflow.com/a/3337928/1964969
is关键字检查类型:
public void DoSomething<TypeP>(TypeP P)
{
if (P is Child1)
{
}
else if(P is Child2)
{
}
}
在您的特定情况下,您想调用Activator.CreatInstance<T>()
但有另一种选择。有一个方法[Activator.CreatInstance(Type type)][1]
.
您可以通过稍微更改您的 dosome 方法来使用它
public void DoSomethingElse(Parent p)
{
var R = Activator.CreateInstance(p.GetType());
}
当然,不知道你在用它做什么,我不知道这是否一定有帮助。您可能仍然需要对 R 类型进行某种 switch 语句,然后才能使用这个新对象。但是,如果从现在开始您可以将R
视为Parent
型,那么这应该对您有所帮助。