C#:创建子类的实例

时间:2014-09-30 06:28:03

标签: c# generics

我有一个像

这样的基类
abstract class A <TEntity, TLog>
{
    public abstract TLog Get(TEntity entity);
}

和少数子类如

class B : A<Branch, BranchLog>
{
    public override BranchLog Get(Branch entity)
    {
        return null;
    }
}

现在可以获取子类的实例,具体取决于它的基类的通用实现,如

class Mapper
{
    public TLog GetMapped<TEntity, TLog>(TEntity entity)
    {

        /*if called with GetMapped<Branch, BranchLog>, make instance of B b = new B()*/
        /*returng b.Get(entity)*/
        return null;
    }
}

或任何方式而不是反思

5 个答案:

答案 0 :(得分:3)

添加一个告诉类型的通用参数。使用new()约束新的通用参数,以便我们可以新建它A<TEntity, TLog>以确保只传递A或其子类的实例。

class Mapper
{
    public TLog GetMapped<TType, TEntity, TLog>(TEntity entity) where TType : A<TEntity, TLog>, new()
    {
        TType instance = new TType();
        return instance.Get(entity);
    }
}

然后将其称为

Mapper mapper = new Mapper();
mapper.GetMapped<B, Branch, BranchLog>(branch);

答案 1 :(得分:1)

  

或任何方式而不是反思

确定。旧式if声明:

public TLog GetMapped<TEntity, TLog>(TEntity entity)
{
    if(typeof(TEntity) == typeof(Branch) && typeof(TLog) == typeof(BranchLog))
    {
        B b = new B();
        return (TLog)b.Get((Branch)entity);
    }
    return default(TLog);
}

但这并不适合使用泛型或继承。您确定自己并未尝试解决XY problem吗?

答案 2 :(得分:0)

这里的问题是除了B之外的任何类都可以扩展通用实例A<Branch,BranchLog>所以这要求编译器说&#34;如果B是唯一的A<Branch,BranchLog>的子类给我一个B&#34;的实例?如果没有其他符号将B专门链接到Mapper类中的特定泛型方法,编译器就无法自行决定。如果你试图完成这个&#34;自动化&#34;没有泛型或Branch,BranchLog - >的其他明确链接B那么你基本上要求编译器自动化相当于:

assembly.GetTypes().Where(t => typeof(B).BaseType == t).FirstOrDefault()

这不是特定于泛型的。我们可以使用简单类A<Branch,BranchLog>替换A1实例,而无需泛型即可重写此问题。

class A {}

class A1 : A {}

class B : A1 {}

class Mapper {
     void Fun(A1) {
         // How to get B b = new B() since I know A1
         // There is no automatic way at compile time, using the type system
     }
}

因为没有什么能阻止我宣布:

class C : A1 {}
class D : A1 {}

现在BCD都是A1的实例。

我认为你需要重新思考你的班级设计。

  1. 如果您无法修改Mapper类,请考虑某种通用工厂类或方法。您应该能够编写具有3个泛型类型参数的工厂方法,这些参数将特定子类(B)映射到用于A<TEntity,TLog>的特定通用实例参数。

    public V GetMapped<T,U,V>(U u) { return new T().Get(U) as V; }

  2. 借助其他机制来决定哪个班级映射到您的&#34;签名&#34;如其他答案中所列(RTTI /是/ as)。但在我看来,如果您已经在使用泛型,那么您应该坚持使用通用解决方案。

答案 3 :(得分:-1)

嗯,你可以在没有反射的情况下做到这一点,但是你需要知道可能分别为TEntity和TLOG推断出的所有可能的类型。

尝试

public TLog GetMapped<TEntity, TLog>(TEntity entity)
{
    if(typeof(TEntity) == typeof(Branch) && typeof(TLog) == typeof(BranchLog))
    {
        B b = new B();
        return b.Get(entity);
    }
    return null;
}

但您也可以在派生类型B中覆盖GetMapped方法,以获取此类的实例,您可以在其中调用b.Get(entity)

但是,如果还有更多这种单一类型(通常情况下是泛型,为什么还要使用它们?!),这不是真正的好选择。

答案 4 :(得分:-1)

有趣的是,通过制作GetMapped&lt;&gt;像这样的泛型,Mapper类的客户端已经必须确切地知道应该存在哪个映射。无论具体实施如何,我都会重新考虑你在这里要做的事情。您是否可以更具体地了解GetMapped&lt;&gt; -method将首先用于什么?

编辑:为了尝试发布替代解决方案,为什么不将子类的特定实例放入Mapper类中的实际Dictionary中:

public class Mapper
{
    private readonly Dictionary<Type, object> _instances;

    public Mapper()
    {
        _instances = new Dictionary<Type, object>()
        {
            { typeof(Branch), new B() },
            ...
        };
    }

    public TLog GetMapped<TEntity, TLog>(TEntity entity)
    {
        A<TEntity, TLog> a = (A<TEntity, TLog>) _instances[typeof(TEntity)];

        return a.Get(entity);
    }
}