Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can a Base Class Method return the type of the derived class?

Based on the other posts I have read, it seems that this may not be possible, but I thought I would post what I am trying to do and see if anyone knows of a solution.

I am trying to add a "Clone()" method to classes generated from a Telerik Open Access domain model. No problem. I was able to figure out how to add a base class to the generated entity models so that I could identify those classes by their base class. (All entities inherit from a base class)

I want ALL these entity model classes to be able to Clone themselves. I have found a solution for that as well. (Deep Cloning Objects)

Now I have a base class and I want to add a Clone() function to every class that derives from that base class. So ... it seems that the base class is the natural place to put it ... right?

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));
    }
}

I add the protected generic Clone() method because at the base class level we don't know the Type we are cloning. The Clone() method needs to be implemented by the entity model itself in order to provide the specific type being cloned.

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

This works fine, but does not guarantee that derived classes will publicly expose a Clone() method, so I added an abstract Clone() method to the base which will require the derived class to implement a public Clone() method.

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();
}

I give it a return type of the base class itself; every implementation MUST return a value which adheres to the DBEntityBase, which of course, all derived classes do. Since the Clone() method is returning a type of the derived class itself then ... it seems to make sense that this would work.

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

When building, however, I get the error ..

'DeliverableEntity' does not implement inherited abstract member 'DBEntityBase.Clone()'

Presumably due to the return type.

I know that I COULD just put the Clone() method in a separate utility class and not implement it directly in each entity model ... that would get me around my problem (and probably save a lot of implementation code), but I am still left wondering why this won't work. It seems like there should be a way to do this.

UPDATE

In response to @Luann's first reply (thank you) I made the change to "override" ...

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

and am now receiving the following error ...

return type must be 'DBEntityBase' to match overridden member 'DBEntityBase.Clone()'

SOLUTION

Thanks to Flynn1179 I was able to move forward again. I thought I would take a moment to document what I did here for future reference ..

Instead of creating a partial class for each entity model in the ORM, implementing an abstract method, I created a single extension method as suggested.

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));
        }
    }
}

Some things to note ...

  • Put this class in the same Namespace as the entity models. If it's in a different namespace then you will need to add that namespace in order to have access to the method.
  • I defined the constraint on the generic type to only and all classes which inherit the DBEntityBase class. Since I made all my entity model classes derive from this base class I know that all of them will will expose this method but also that any class which does NOT derive from this class will NOT have this capability.
  • The extension method and the class that contains it must be static

Now the cool part is that ALL the Entities have access to the function ...

    // 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();

I love this solution as it's has the simplicity of a base class where the implementation is defined in a single location (whereas I was looking at implementing an abstract method individually in each derived class) plus since it's associated with the DBEntityBase class and in the same namespace, it becomes part of the "contract' defined by the base class which means I can count on it being available whenever I have a class derived from DBEntityBase.

like image 715
Gary O. Stenstrom Avatar asked Aug 20 '15 15:08

Gary O. Stenstrom


People also ask

Can a base class reference to derived class?

// As base-class pointer cannot access the derived class variable.

Can base class access derived class private?

Derived class can not access the private members of it's base class. No type of inheritance allows access to private members.

Can you assign a base class object to a derived class reference?

No, it is not possible. Consider a scenario where an ACBus is a derived class of base class Bus.


1 Answers

By popular request..

Try an extension method:

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

I have to be honest, I didn't think this would work, as I expected C# would be unable to determine exactly what it's an extension of. Apparently however, it does!

like image 95
Flynn1179 Avatar answered Oct 27 '22 14:10

Flynn1179