【问题标题】:Use type of concrete implementation in abstract base class在抽象基类中使用具体实现类型
【发布时间】:2015-11-12 12:04:39
【问题描述】:

有什么方法可以在这里做我想做的事吗?

基类定义如下:

public abstract class BaseClass<TExists>
    where TExists : BaseExists
{
    // needs to be overridden by child
    protected abstract bool Exists(TExists existsData, out /*typeof(this)*/ existingElement); // <- how to have the concrete type here?

    // static method to be invoked without any need of an instance
    public static bool Exists(TExists existsData, out /*typeof(this)*/ existingElement)
    {
        var temp; // <-- how to set the type here?

        // create a concrete instance
        var instance = Activator.CreateInstance(???);

        // call the concrete implementation
        if(instance.Exists(existsData, out temp))
        {
            return true;
        }

        return false;
    }
}

这里我们有一些具体的实现:

public class ChildClass : BaseClass<ChildClassExists>
{
    protected override bool Exists(ChildClassExists exists, out ChildClass existingElement)
    {
        // do child-related things here
    }
}

最后我想像这样使用它

ChildClass existing;    
if(ChildClass.Exists(new ChildClassExists(), out existing)){
    // do things here with the existing element of type 'ChildClass'
}

因为我这里不需要实例(这隐藏在 Exists 的基类实现中)。

更新 #1

正如在 InBetweens 第一个答案中一样,我现在有了:

public static bool Exists<T>(TExists existsModel, out T existingEntityModel)
    where T : BaseClass<TExists>
{
    var instance = Activator.CreateInstance<T>();
    return instance.ExistsInternal(existsModel, out existingEntityModel);
}

protected abstract bool ExistsInternal<T>(TExists createModel, out T existingEntityModel)
    where T : BaseClass<TExists>;

但这会在 ExistsInternal 方法的具体实现中引发错误

无法将源类型“ChildClass”转换为目标类型“T”

在覆盖中

protected override bool ExistsInternal<T>(ChildClassExists existsData, out T existingElement)
{
    existingElement = new ChildClass(); // <-- here the error is thrown
    return true;
}

【问题讨论】:

  • 您必须添加另一个通用参数 (TConcrete)。但总的来说,问题意味着糟糕的设计。此外,您可以使用new TConcrete() 代替Activator.CreateInstance()(假设您可以添加new() 约束)
  • 你试过out TExists existingElementTExists temp;吗?
  • @FabioLuz: TExists 是包含存在检查数据的对象的基本类型,而不是应该返回的对象的类型-
  • @haim770:为什么这个设计不好?我必须确保每个具体实现都有一个Exists,但不需要创建实例来调用它。另一方面,什么是更好的解决方法?
  • public static bool Exists&lt;T&gt;(TExists existsData, out T existingElement) where T: BaseClass&lt;TExists&gt;?

标签: c# generics inheritance abstract-class


【解决方案1】:

只需在Exists 方法中添加一个新的泛型参数即可。这种类型将由编译器推断,因此对可用性没有实际影响:

public abstract class BaseClass<TExists> where TExists : BaseExists
{
    // needs to be overridden by child
    protected abstract bool InternalExistsCheck<T>(TExists existsData, out T existingElement) where T : BaseClass<TExists>, new();

    // static method to be invoked without any need of an instance
    public static bool Exists<T>(TExists existsData, out T existingElement) where T : BaseClass<TExists>, new()
    {
        T temp; // <-- how to set the type here?
        existingElement = null;

        // create a concrete instance
        var instance = new T();

        // call the concrete implementation
        if (instance.InternalExistsCheck(existsData, out temp))
        {
            existingElement = temp;
            return true;
        }

        return false;
    }
}

请注意,如果您不更改受保护的 Exists 方法,您将收到一个模棱两可的调用编译时错误(VS 2013)。

现在,一切都很好:

public class ChildClass : BaseClass<ChildClassExists>
{
    protected override bool InternalExistsCheck<T>(ChildClassExists exists, out T existingElement)
    {
        ....
    }
}

ChildClass existing;

if (ChildClass.Exists(new ChildClassExists(), out existing))
{
     // do things here with the existing element of type 'ChildClass'
}

更新

解决您对无法在覆盖的ChildClass.InternalExistsCheck(,) 中将ChildInstance 分配给existing 的担忧,是的,您只需执行以下操作即可:

existing = new T();

如果TChildClass(由编译器推断),那么您将创建一个ChildClass 实例。请记住,虽然您得到的是 BaseClass&lt;ChildExists&gt; 类型的引用,而不是 ChildClass

如果您绝对需要 ChildClass 类型的引用,那么有一个解决方法(如果您需要这样做,可能是因为泛型不是适合您的工具):

var childClassTypedReference = (object)existing as ChildClass.

请意识到整个解决方案并不像您希望的那样类型安全;您必须考虑existing 不是ChildClass 类型引用的可能性(因此childClassTypedReferencenull)。没有什么可以阻止 existing 成为 any 类型扩展 BaseClass&lt;ChildExists&gt;

我没有你的代码的全貌,但我真的认为你在这里滥用泛型。我认为具有IExists 接口依赖项的非通用方法将是一种更简洁的方法。

【讨论】:

  • default(T) 在这种情况下总是会返回 null 并且你会在下一行得到一个 NRE
  • @haim7770:是的,它的new T()。没有正确的想法:p 谢谢
  • 我仍然对T 类型的out 参数有问题,无法在ChildClasss 覆盖InternalExistsCheck 中设置该参数。看不到我错过了什么。
  • @KingKerosin 记住,在方法返回之前必须给 out 参数设置一个值
  • @KingKerosin 您不能将ChildClass 类型化的实例分配给T,因为T 的类型实际上是BaseClass&lt;ChildExists&gt;。 C# 不允许重写方法中的类型变化(这不是泛型限制)。您可以规避这种滥用泛型系统的情况,但这并不值得头疼。
【解决方案2】:

受 cmets/answers 的启发,我想出了以下完全符合我需求的解决方案。

1 添加了一个新的(非泛型)基类BaseClass

2 让泛型基类继承自非泛型

public abstract class BaseClass<TExists> : BaseClass
{
}

3 在BaseClass&lt;TExists&gt; 中设置abstractstatic 方法

// abstract to be overriden by child-classes
protected abstract BaseClass ExistsInternal(TExists existsModel);

public static bool Exists<T>(TExists existsModel, out T existing) where T : BaseClass<TExists>
{
    // create a new instance of the concrete child
    var instance = (T) Activator.CreateInstance<T>;

    // set the out parameter (can be null)
    existing = instance.ExistsInternal(existsModel) as T;

    // return if already existing
    return existing!= null;
}

4 在子类中实现覆盖

protected override BaseClass ExistsInternal(ChildClassExists existsModel)
{
    // check for existing
    var existing = ...; // do child-class dependend stuff

    // return concrete instance or null
    return existing != null ? new ChildClass(existing) : null;
}

5 调用它(通过ChildClass 使用推断的类型)

ChildClass childClass;
if (ChildClass.Exists(childClassExists, out childClass))
{
    // do things with existing childClass
}

6 对 InBetween 和 haim7770 表示感谢

@InBetween, @haim7770 -> 谢谢! ;)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-08-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-09
    • 2018-12-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多