基类方法可以返回派生类的类型吗?

时间:2015-08-20 15:42:33

标签: c# inheritance base-class

基于我读过的其他帖子,似乎这可能是不可能的,但我想我会发布我想要做的事情,看看是否有人知道解决方案。

我正在尝试向Telerik Open Access域模型生成的类添加“Clone()”方法。没问题。我能够弄清楚如何将基类添加到生成的实体模型中,以便我可以通过它们的基类来识别这些类。 (All entities inherit from a base class

我希望所有这些实体模型类都能够克隆自己。我也找到了解决方案。 (Deep Cloning Objects

现在我有了一个基类,我想在每个派生自该基类的类中添加一个Clone()函数。所以......似乎基类是放置它的自然场所......对吗?

public abstract class DBEntityBase
{
    /// <summary>
    ///     Gets a COPY of the instance in it's current state
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    protected T Clone<T>()
    {
        return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(this));
    }
}

我添加了受保护的泛型Clone()方法,因为在基类级别我们不知道我们正在克隆的类型。 Clone()方法需要由实体模型本身实现,以便提供克隆的特定类型。

public partial class DeliverableEntity
{
    public new DeliverableEntity Clone()
    {
        return this.Clone<DeliverableEntity>();
    }
}

这很好,但 保证 派生类会公开公开Clone()方法,所以我添加了一个抽象Clone()基础的方法, 需要 派生类来实现公共Clone()方法。

public abstract class DarkRoomDBEntityBase
{
    /// <summary>
    ///     Gets a COPY of the instance in it's current state
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    protected T Clone<T>()
    {
        return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(this));
    }

    /// <summary>
    ///     Gets a deep COPY of the entity instance
    /// </summary>
    /// <returns></returns>
    public abstract DBEntityBase Clone();
}

我给它一个基类本身的返回类型;每个实现必须返回一个遵循DBEntityBase的值,当然,所有派生类都会这样做。由于Clone()方法返回一个派生类本身的类型,然后...似乎有意义这可行。

DeliverableEntity originalEntity = new DeliverableEntity();
DeliverableEntity clonedEntity   = originalEntity.Clone();

然而,在构建时,我收到错误..

  

'DeliverableEntity'不实现继承的抽象成员   'DBEntityBase.Clone()'

大概是由于返回类型。

我知道我可能只是将Clone()方法放在一个单独的实用程序类中,而不是直接在每个实体模型中实现它...这会让我解决我的问题(并且可能会节省大量的实现代码),但我仍然想知道为什么这不起作用。似乎应该有办法做到这一点。

更新

回应@Luann的第一个回复(谢谢)我做了更改“覆盖”......

public partial class DeliverableEntity
{
    public override DeliverableEntity Clone()
    {
        return this.Clone<DeliverableEntity>();
    }
}

现在收到以下错误......

  

返回类型必须是'DBEntityBase'才能匹配被覆盖的成员   'DBEntityBase.Clone()'

解决方案

感谢Flynn1179,我能够再次向前迈进。我想我会花一点时间来记录我在这里做了什么,以备将来参考..

我没有为ORM中的每个实体模型创建一个部分类,而是实现了一个抽象方法,而是按照建议创建了一个扩展方法。

namespace DAL
{
    public partial class DeliverableEntity : DBEntityBase
    {
         // ... Code generated from ORM 
    }

    public partial class DeliverableItemEntity : DBEntityBase
    {
         // ... Code generated from ORM 
    }

    public partial class DeliverableItemAttrEntity : DBEntityBase
    {
         // ... Code generated from ORM 
    }
}

namespace DAL
{
    public static class EntityExtensionMethods
    {
        public static T Clone<T>(this T entity) where T: DBEntityBase
        {
            return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(entity));
        }
    }
}

有些事要注意......

  • 将此类放在与实体模型相同的命名空间中。如果它位于不同的命名空间中,那么您需要添加该命名空间才能访问该方法。
  • 我将泛型类型的约束定义为仅继承 DBEntityBase 类的所有类。由于我将所有实体模型类派生自此基类,因此我知道所有这些类都将公开此方法,但是任何不从此类派生的类都不具备此功能。
  • 扩展方法和包含它的类必须是 static

现在很酷的部分是所有实体都可以访问该功能......

    // Create the original instances of the entities    
    DeliverableEntity origDeliverable       = new DeliverableEntity();
    DeliverableItemEntity origItem          = new DeliverableItemEntity();
    DeliverableItemAttrEntity origItemAttr  = new DeliverableItemAttrEntity();

    // now here's the magic 

    DeliverableEntity cloneDeliverable      = origDeliverable.Clone();
    DeliverableItemEntity cloneItem         = origItem.Clone();
    DeliverableItemAttrEntity cloneItemAttr = origItemAttr.Clone();

我喜欢这个解决方案,因为它具有基类的简单性,其中实现在一个位置定义(而我正在考虑在每个派生类中单独实现一个抽象方法),因为它与DBEntityBase类相关联,在同一名称空间中,它成为基类定义的“契约”的一部分,这意味着只要我有一个派生自DBEntityBase的类,我就可以指望它可用。

2 个答案:

答案 0 :(得分:7)

受欢迎的要求..

尝试使用扩展方法:

public T Clone<T>(this T obj) where T : DBEntityBase
{
  return /* insert code that creates clone here */
}

我必须诚实,我并不认为这会起作用,因为我预计C#无法准确确定它的延伸是什么。显然,确实如此!

答案 1 :(得分:0)

新的C#版本9.0将支持covariant returns,这意味着您可以覆盖Clone()并返回更特定的类型。这将编译:

public partial class DeliverableEntity
{
    public override DeliverableEntity Clone()
    {
        return this.Clone<DeliverableEntity>();
    }
}