tags:

views:

2223

answers:

5

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

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

+24  A: 

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.

Andrey Shchekin
I disagree, bad or not bad, it's very useful sometime for SHALLOW clonning, and in those cases is really needed the Clone<T>, to save an unnecessary boxing and unboxing.
Shimmy
A: 

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();
}
Mauricio Scheffer
Easy enough to include in the answe...oh, wait...
Jeff Yates
I'd rather link it to respect the copyright...
Mauricio Scheffer
I included a snippet since because of the honor of the copy-rights the link was broken...
Shimmy
And how exactly is that interface supposed to work? For the result of a clone operation to be castable to type T, the object being cloned has to derive from T. There's no way to set up such a type constraint. Realistically speaking, the only thing I can see the result of iCloneable returning is a type iCloneable.
supercat
@supercat: yes, that's exactly what Clone() returns: a T which implements ICloneable<T>. MbUnit has been using this interface for *years*, so yes, it works. As for the implementation, take a peek into MbUnit's source.
Mauricio Scheffer
@Mauricio Scheffer: I see what's going on. No type actually implements iCloneable(of T). Instead, each type implements iCloneable(of itself). That would work, I guess, though all it does is move a typecast. It's too bad there's no way to declare a field as having an inheritance constraint and an interface one; it would be helpful to have a cloning method return an object of a semi-cloneable (cloning exposed as Protected method only) base type, but with a cloneable type constraint. That would allow an object to accept cloneable derivatives of semi-cloneable derivatives of a base type.
supercat
+27  A: 

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?

Marc Gravell
+1 I always thought that the covariance problem was the real reason
DrJokepu
There is a problem with this: the explicit interface implementation must be _private_, which would cause problems should Foo at some point need to be cast to ICloneable... Am I missing something here?
Joel in Gö
Why would that pose a problem, exactly? I don't follow your logic...
Marc Gravell
My mistake: it turns out that, despite being defined as private, the method _will_ be called should Foo be cast to IClonable (CLR via C# 2nd Ed, p.319). Seems like an odd design decision, but there it is. So Foo.Clone() gives the first method, and ((ICloneable) Foo).Clone() gives the second method.
Joel in Gö
Actually, explicit interface implementations are, strictly speaking, part of the public API. In that they are callable publicly via casting.
Marc Gravell
Yes. Has a bad smell though, IMHO: methods that are declared private should be private, dammit.
Joel in Gö
OK; show me where it is "declared private" ;-p
Marc Gravell
From 20.4.1: Explicit interface member implementations have different accessibility characteristics than other members.Because explicit interface member implementations are never accessible through a qualified interfacemember name in a method invocation or a property access, they are in a sense...
Marc Gravell
...private. However, since theycan be accessed through an interface instance, they are in a sense also public.
Marc Gravell
QED ;) (why min 10 character for a comment?)
Joel in Gö
A: 

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.

baretta
"bad" does not mean deprecated. It simply mean "you're better off not using it". It's not deprecated in the sense that "we'll remove the ICloneable interface in the future". They just encourage you not to use it, and won't update it with generics or other new features.
jalf
Dennis: See Marc's answers. Covariance / inheritance issues make this interface pretty much unusable for any scenario that is at least marginally complicated.
DrJokepu
Yea, i can see the limitations of ICloneable, for sure... I rarely need to use cloning, though.
baretta
+5  A: 

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 ( object.ReferenceEquals( clone, null ) ) { throw new NullReferenceException( "Clone was not created." ); }
        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(); }
}
TcKs
Typo - s/clonse/clone
David Gardiner
@David Gardiner: Corrected, thanks.
TcKs
@TcKs: Any workable clone method for an inheritable class must use Object.MemberwiseClone as a starting point (or else use reflection) since otherwise there's no guarantee that the clone will be the same type as the original object. I have a pretty nice pattern if you're interested.
supercat