Why no ICloneable<T>?

Viewed 53738

Is there a particular reason why a generic ICloneable<T> does not exist?

It would be much more comfortable, if I would not need to cast it everytime I clone something.

8 Answers

In addition to Andrey's reply (which I agree with, +1) - when ICloneable is done, you can also choose explicit implementation to make the public Clone() return a typed object:

public Foo Clone() { /* your code */ }
object ICloneable.Clone() {return Clone();}

Of course there is a second issue with a generic ICloneable<T> - inheritance.

If I have:

public class Foo {}
public class Bar : Foo {}

And I implemented ICloneable<T>, then do I implement ICloneable<Foo>? ICloneable<Bar>? You quickly start implementing a lot of identical interfaces... Compare to a cast... and is it really so bad?

ICloneable is considered a bad API now, since it does not specify whether the result is a deep or a shallow copy. I think this is why they do not improve this interface.

You can probably do a typed cloning extension method, but I think it would require a different name since extension methods have less priority than original ones.

I think the question "why" is needless. There is a lot of interfaces/classes/etc... which is very usefull, but is not part of .NET Frameworku base library.

But, mainly you can do it yourself.

public interface ICloneable<T> : ICloneable {
    new T Clone();
}

public abstract class CloneableBase<T> : ICloneable<T> where T : CloneableBase<T> {
    public abstract T Clone();
    object ICloneable.Clone() => return this.Clone();
}

public abstract class CloneableExBase<T> : CloneableBase<T> where T : CloneableExBase<T> {
    protected abstract T CreateClone();
    protected abstract void FillClone(T clone);
    public override T Clone() {
        T clone = this.CreateClone();
        if (clone is null ) {
            throw new NullReferenceException( "Clone was not created." );
        }

        this.FillClone(clone);
        return clone
    }
}

public abstract class PersonBase<T> : CloneableExBase<T> where T : PersonBase<T> {
    public string Name { get; set; }

    protected override void FillClone( T clone ) {
        clone.Name = this.Name;
    }
}

public sealed class Person : PersonBase<Person> {
    protected override Person CreateClone() => return new Person();
}

public abstract class EmployeeBase<T> : PersonBase<T> where T : EmployeeBase<T> {
    public string Department { get; set; }

    protected override void FillClone(T clone) {
        base.FillClone(clone);

        clone.Department = this.Department;
    }
}

public sealed class Employee : EmployeeBase<Employee> {
    protected override Employee CreateClone() => return new Employee();
}

It's pretty easy to write the interface yourself if you need it:

public interface ICloneable<T> : ICloneable
        where T : ICloneable<T>
{
    new T Clone();
}

It's a very good question... You could make your own, though:

interface ICloneable<T> : ICloneable
{
  new T Clone ( );
}

Andrey says it's considered a bad API, but i have not heard anything about this interface becoming deprecated. And that would break tons of interfaces... The Clone method should perform a shallow copy. If the object also provides deep copy, an overloaded Clone ( bool deep ) can be used.

EDIT: Pattern i use for "cloning" an object, is passing a prototype in the constructor.

class C
{
  public C ( C prototype )
  {
    ...
  }
}

This removes any potential redundant code implementation situations. BTW, talking about the limitations of ICloneable, isn't it really up to the object itself to decide whether a shallow clone or deep clone, or even a partly shallow/partly deep clone, should be performed? Should we really care, as long as the object works as intended? In some occasions, a good Clone implementation might very well include both shallow and deep cloning.

Related