在 List 对象的泛型函数中使用子类型<Parent>



我的问题类似于使用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'
    }
}

我希望用 C1C2 的类型而不是"父"类型来调用 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的例子

这里有两个选项:

  1. 将 DoSomethingElse 替换为 Parent 类中的虚拟方法,因此任何子级都必须覆盖您需要的行为。然后,通常只需通过父类型实例(Liskov 替换原则(调用该重写方法。首选方式,恕我直言。

    public void DoSomething<TypeP>(TypeP P)
    {
        P.DoSomethingElse(); // this is virtual method to be overwritten by children types
    }
    
  2. "泛型专业化"方法(这对我来说似乎有点笨拙,因为 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型,那么这应该对您有所帮助。

相关内容

最新更新