DesertFoxAZ
DesertFoxAZ

Reputation: 489

Creating an abstract generic method

I have a C# class with a method such as below:

public class MyType
{
    public MyType Clone()
    {
        var clone = (MyType)MemberwiseClone();

        // Do some other stuff here with the clone's properties

        return clone;
    }
}

I have a bunch of other classes where I want to implement the Clone method so I was thinking I could create an abstract base class where I could define the Clone method generically so I don't have to put a concrete implementation in each class.

I would think this is possible but I haven't worked too much with generics and my attempts to do this in the past (months ago, so discarded my code out of frustration) haven't been successful.

Is this possible? If so, how could it be done?

Upvotes: 0

Views: 681

Answers (3)

Scott Hannen
Scott Hannen

Reputation: 29207

The usefulness of this approach depends significantly on what it is that you're cloning.

Here's a method that I use. The cloning method is a bit crude. It's specifically for objects that are meant to be serialized as JSON.

That's why the generic constraint (TEntity : BaseEntity) is there. I don't want just anything passed into this method, only something that I know is serializable.

I also avoided using the generic argument for JsonConvert.DeserializeObject because while I want to cast the result as a specific type, I don't want to pass in an inherited type and get back an instance of a base type.

public static TEntity CloneEntity<TEntity>(this BaseEntity input) where TEntity 
    : BaseEntity
{
    if (input == null) return null;
    var serialized = JsonConvert.SerializeObject(input);
    return (TEntity)JsonConvert.DeserializeObject(serialized, input.GetType());
}

Although it's already been accepted, I don't recommend adding this to a base class unless absolutely necessary. Before long you might find that you need to clone something that already inherits from a different base class.

This requires the Newtonsoft.JSON package.

As mentioned in a comment, this will do a deep clone. As I stated at the top, this method applies only if serialization/deserialization cloning is appropriate to the types you need to clone. If there were a universally applicable way to clone objects that applied in every case then object would probably have a public Clone method.

If we're cloning classes it's likely because they contain data, and where that's the case deep cloning is likely preferable. For example, suppose we have a Customer class, and one if its properties exposes an Address object. MemberwiseClone will clone the value types, but will result in two Customer objects that share a reference to the same Address. If we're cloning it's usually because we're trying to create entirely distinct objects. If we think we've cloned something but beneath the surface the original and clone share object references then there's a good chance we'll have bugs.

Upvotes: 2

Olivier Jacot-Descombes
Olivier Jacot-Descombes

Reputation: 112279

Built on @Gusman's solution I add the possibility to do some initialization

public abstract class ClonableBase<T>
{
    public T Clone()
    {
        T clone = (T)MemberwiseClone();
        Initialize(clone)
        return clone;
    }

    protected virtual void Initialize(T clone)
    {
    }
}

If the initialization is mandatory, you can also make Initialize abstract instead.

public class RealClass : ClonableBase<RealClass> {
    protected override void Initialize(T clone)
    {
        // Do some other stuff here with the clone's properties
    }    
}

Upvotes: 0

Gusman
Gusman

Reputation: 15151

Create an abstract generic base and then implement the concrete type on the derived ones:

public abstract class ClonableBase<T>
{
    public T Clone()
    {
        return (T)this.MemberwiseClone();
    }
}

public class RealClass : ClonableBase<RealClass> { }

Upvotes: 5

Related Questions