Why no ICloneable<T>?
Asked Answered
F

8

264

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.

Fancied answered 11/2, 2009 at 11:13 Comment(4)
No! with all due respect to the 'reasons', I agree with you, they should have implemented it!Cyzicus
It would have been a nice thing for Microsoft to have defined (the problem with brew-your-own interfaces is that interfaces in two different assemblies will be incompatible, even if they're semantically identical). Were I designing the interface, it would have three members, Clone, Self, and CloneIfMutable, all of which would return a T (the last member would either return Clone or Self, as appropriate). The Self member would make it possible to accept an ICloneable(of Foo) as a parameter and then use it as a Foo, without need for a typecast.Dowie
That would allow for a proper cloning class hierarchy, where inheritable classes expose a protected "clone" method, and have sealed derivatives that expose a public one. For example, one could have Pile, CloneablePile:Pile, EnhancedPile:Pile, and CloneableEnhancedPile:EnhancedPile, none of which would be broken if cloned (even though not all expose a public cloning method), and FurtherEnhancedPile:EnhancedPile (which would be broken if cloned, but doesn't expose any cloning method). A routine that accepts an ICloneable(of Pile) could accept a CloneablePile or a CloneableEnhancedPile...Dowie
...even though CloneableEnhancedPile does not inherit from CloneablePile. Note that if EnhancedPile inherited from CloneablePile, FurtherEnhancedPile would have to expose a public cloning method and could be passed to code that would expect to Clone it, violating the Liskov Substitutability Principle. Since CloneableEnhancedPile would implement ICloneable(Of EnhancedPile) and by implication ICloneable(Of Pile), it could be passed to a routine expecting a cloneable derivative of Pile.Dowie
B
124

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.

Baptist answered 11/2, 2009 at 11:16 Comment(19)
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.Cyzicus
@Merlyn I think it should be a separate question. One answer may be BinaryFormatter Serialize/Deserialize for deep cloning, but it really depends on task, required performance, etc.Baptist
What do you mean by extension methods have less priority than original ones.Isometry
@AndreyShchekin: What's unclear about deep vs shallow cloning? If List<T> had a clone method, I would expect it to yield a List<T> whose items have the same identities as those in the original list, but I would expect that any internal data structures would be duplicated as needed to ensure that nothing done to one list will affect the identities of items stored in the other. Where's the ambiguity? A bigger problem with cloning comes with a variation of the "diamond problem": if CloneableFoo inherits from [not publicly cloneable] Foo, should CloneableDerivedFoo derive from...Dowie
@AndreyShchekin: CloneableFoo or from [not publicly cloneable] DerivedFoo? I would suggest that a generic ICloneable<out T> could resolve that issue if it derives from ISelf<out T> which contains a Self property of type T that simply returns itself. In that case, someone needing a cloneable DerivedFoo could accept a parameter of type ICloneable<DerivedFoo>.Dowie
@supercat: I can not say I understand your expectation fully, as what will you consider an identity of the list itself, for example (in case of list of lists)? However, ignoring that, your expectation is not the only possible idea people may have when calling or implementing Clone. What if library authors implementing some other list do not follow your expectation? The API should be trivially unambiguous, not arguably unambiguous.Baptist
@AndreyShchekin: Suppose an automobile repair shop has a list of all cars that are in for service at any given moment. Such a list would be mutable. Someone is asked to take a "snapshot" of the list of cars in the shop at the some particular moment. Writing "the whiteboard in the shop" on a slip of paper (making a duplicate reference to the list) would clearly not suffice, since the next time the list is changed it would no longer represent a list of cars that had been in the shop when the snapshot was supposed to be taken. On the other hand, ...Dowie
@AndreyShchekin: ...someone asked to make a snapshot of the list would not be expected to manufacture duplicates of all the cars thereon. The cars themselves are mutable objects, with many mutable properties, though not all properies are mutable. The make and model, for example, would be immutable, so a snapshot of the list would capture the make and model of the cars left overnight. One would not expect the duplicate of the list to capture any mutable properties (e.g. odometer reading), however, unless the list itself contained explicit copies of them.Dowie
@AndreyShchekin: An important thing to note, though, is that making duplciate cars and writing down the VINs of those duplicate cars would not only require a lot of extra work--it would also be fundamentally wrong. Given daily lists of VINs, one could determine out how many times a particular car has stayed in the shop overnight. If every snapshot of the list contained the VINs of cars that were manufactured to match the cars that were brought in, however, such analysis would be impossible, since such a list would no longer record the identity of the cars that were brought in.Dowie
@Dowie So you are talking about shallow copy. However, there is nothing fundamentally wrong with a deep copy, for example if you want to do a reversible transaction on in-memory objects. Your expectation is based on your use case, other people may have different expectations. If they put your objects into their objects (or vice versa), results will be unexpected.Baptist
@AndreyShchekin: The term "shallow copy" is sometimes used to refer to what I described, and sometimes used to refer to the result of MemberwiseClone on a class or copying a structure. While there are situations where deeper copying would be appropriate, such scenarios would entail a much tighter copying of the collection and its contents than would be implied by something like a List<T>, and if one is going to that level one should go beyond Clone, using an interface with members AsImmutable() and AsMutable(); calling AsImmutable on an object should yield an immutable snapshot.Dowie
@AndreyShchekin: An immutable object is a snapshot of its own state, and could thus return itself. A mutable object should create an immutable snapshot and return that. If desired, it could cache that snapshot and, the next time AsImmutable is called, return the cached snapshot if it was still good. Even if the only way to tell whether the cached snapshot was still valid was to generate a new immutable object and compare it, returning the cached snapshot would expedite comparisons between it and the earlier one. A good weak interning collection could improve things even further.Dowie
@AndreyShchekin: Unconditional deep-cloning of mutable objects should IMHO be regarded as an anti-pattern. While aggressive copy-on-write approaches are not good for performance on multi-threaded systems, using shared immutable objects for the portion of a deep hierarchy which are held in common between instances can be a big win.Dowie
@Dowie you do not take into account it is a part of .NET framework, not part of your application. so if you make a class that does not do deep cloning, then someone else makes a class that does do deep cloning and calls Clone on all its parts, it will not work predictably -- depending on whether that part was implemented by you or that person who likes deep cloning. your point about the patterns is valid but having IMHO in API is not clear enough -- it either should be called ShallowCopy to stress the point, or not provided at all.Baptist
@AndreyShchekin: If I had an object which implemented IList<T> and ICloneable, and T was a class type, I would expect that cloning should give me a new list which held references to the same object instances as the original. I would regard a clone function that did anything else as broken. For an object to clone deeper than its semantic contents is not "optional"--it is a breaking behavior.Dowie
@AndreyShchekin: I suppose perhaps the biggest problem is that Microsoft didn't simply specify that ICloneable should work in the only fashion that would be logical and useful in the general case (which is what I described), but I can't see any reason why it should be expected to work in any other way. Of course, that would hardly be the only case where Microsoft has been less than optimal in its semantic specifications. Object equality has the same issue. It's not hard to define an equivalence relation which would be unambiguously applicable to every single type of object, but...Dowie
...Microsoft put a few quirks in a couple types' implementation of Equals. A proper equivalence relation would specify that two objects should compare "equal" if and only if they would be semantically indistinguishable except by checking reference equality. Having such an equivalence relation available for all classes would facilitate things like interning classes (given two objects compare equal, one of which is in an interning dictionary, one would be able to substitute the other safely). Too bad the Object.Equals overrides of float, double, and Decimal don't work that way.Dowie
FWIW I came to this question in search of a canonical method to clone a list such that I could alter properties of its members without altering the members in the original list.Maggs
Lots of valid reasons why it's a bad idea. However, in the real world, you sometimes need it. For example, in .net Workflow Foundation, I need work with an object, modify it's content deep in some library code, and then give it back to WF. If it's the same reference, it won't detect changes :( so I have to make a copy of it, and a shallow copy is enough.Polypody
D
158

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?

Dogface answered 11/2, 2009 at 11:19 Comment(11)
+1 I always thought that the covariance problem was the real reasonStreusel
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?Torchbearer
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.Torchbearer
Actually, explicit interface implementations are, strictly speaking, part of the public API. In that they are callable publicly via casting.Dogface
Yes. Has a bad smell though, IMHO: methods that are declared private should be private, dammit.Torchbearer
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 interface member name in a method invocation or a property access, they are in a sense...Dogface
...private. However, since they can be accessed through an interface instance, they are in a sense also public.Dogface
QED ;) (why min 10 character for a comment?)Torchbearer
The proposed solution seems great at first... until you realize that in a class hierarchy, you want 'public Foo Clone()' to be virtual and overridden in derived classes so they can return their own type (and clone their own fields of course). Sadly, you can't change the return type in an override (the covariance problem mentioned). So, you come full circle back to the original problem again - the explicit implementation doesn't really buy you much (other than returning the base type of your hierarchy instead of 'object'), and you're back to casting most of your Clone() call results. Yuck!Handbook
In regards to your answers, on the line object ICloneable.Clone() {return Clone();}, how is it that Foo Clone() would be called, and not ICloneable.Clone() recursively?Celio
@Bruno because : explicit interface implementationDogface
B
124

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.

Baptist answered 11/2, 2009 at 11:16 Comment(19)
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.Cyzicus
@Merlyn I think it should be a separate question. One answer may be BinaryFormatter Serialize/Deserialize for deep cloning, but it really depends on task, required performance, etc.Baptist
What do you mean by extension methods have less priority than original ones.Isometry
@AndreyShchekin: What's unclear about deep vs shallow cloning? If List<T> had a clone method, I would expect it to yield a List<T> whose items have the same identities as those in the original list, but I would expect that any internal data structures would be duplicated as needed to ensure that nothing done to one list will affect the identities of items stored in the other. Where's the ambiguity? A bigger problem with cloning comes with a variation of the "diamond problem": if CloneableFoo inherits from [not publicly cloneable] Foo, should CloneableDerivedFoo derive from...Dowie
@AndreyShchekin: CloneableFoo or from [not publicly cloneable] DerivedFoo? I would suggest that a generic ICloneable<out T> could resolve that issue if it derives from ISelf<out T> which contains a Self property of type T that simply returns itself. In that case, someone needing a cloneable DerivedFoo could accept a parameter of type ICloneable<DerivedFoo>.Dowie
@supercat: I can not say I understand your expectation fully, as what will you consider an identity of the list itself, for example (in case of list of lists)? However, ignoring that, your expectation is not the only possible idea people may have when calling or implementing Clone. What if library authors implementing some other list do not follow your expectation? The API should be trivially unambiguous, not arguably unambiguous.Baptist
@AndreyShchekin: Suppose an automobile repair shop has a list of all cars that are in for service at any given moment. Such a list would be mutable. Someone is asked to take a "snapshot" of the list of cars in the shop at the some particular moment. Writing "the whiteboard in the shop" on a slip of paper (making a duplicate reference to the list) would clearly not suffice, since the next time the list is changed it would no longer represent a list of cars that had been in the shop when the snapshot was supposed to be taken. On the other hand, ...Dowie
@AndreyShchekin: ...someone asked to make a snapshot of the list would not be expected to manufacture duplicates of all the cars thereon. The cars themselves are mutable objects, with many mutable properties, though not all properies are mutable. The make and model, for example, would be immutable, so a snapshot of the list would capture the make and model of the cars left overnight. One would not expect the duplicate of the list to capture any mutable properties (e.g. odometer reading), however, unless the list itself contained explicit copies of them.Dowie
@AndreyShchekin: An important thing to note, though, is that making duplciate cars and writing down the VINs of those duplicate cars would not only require a lot of extra work--it would also be fundamentally wrong. Given daily lists of VINs, one could determine out how many times a particular car has stayed in the shop overnight. If every snapshot of the list contained the VINs of cars that were manufactured to match the cars that were brought in, however, such analysis would be impossible, since such a list would no longer record the identity of the cars that were brought in.Dowie
@Dowie So you are talking about shallow copy. However, there is nothing fundamentally wrong with a deep copy, for example if you want to do a reversible transaction on in-memory objects. Your expectation is based on your use case, other people may have different expectations. If they put your objects into their objects (or vice versa), results will be unexpected.Baptist
@AndreyShchekin: The term "shallow copy" is sometimes used to refer to what I described, and sometimes used to refer to the result of MemberwiseClone on a class or copying a structure. While there are situations where deeper copying would be appropriate, such scenarios would entail a much tighter copying of the collection and its contents than would be implied by something like a List<T>, and if one is going to that level one should go beyond Clone, using an interface with members AsImmutable() and AsMutable(); calling AsImmutable on an object should yield an immutable snapshot.Dowie
@AndreyShchekin: An immutable object is a snapshot of its own state, and could thus return itself. A mutable object should create an immutable snapshot and return that. If desired, it could cache that snapshot and, the next time AsImmutable is called, return the cached snapshot if it was still good. Even if the only way to tell whether the cached snapshot was still valid was to generate a new immutable object and compare it, returning the cached snapshot would expedite comparisons between it and the earlier one. A good weak interning collection could improve things even further.Dowie
@AndreyShchekin: Unconditional deep-cloning of mutable objects should IMHO be regarded as an anti-pattern. While aggressive copy-on-write approaches are not good for performance on multi-threaded systems, using shared immutable objects for the portion of a deep hierarchy which are held in common between instances can be a big win.Dowie
@Dowie you do not take into account it is a part of .NET framework, not part of your application. so if you make a class that does not do deep cloning, then someone else makes a class that does do deep cloning and calls Clone on all its parts, it will not work predictably -- depending on whether that part was implemented by you or that person who likes deep cloning. your point about the patterns is valid but having IMHO in API is not clear enough -- it either should be called ShallowCopy to stress the point, or not provided at all.Baptist
@AndreyShchekin: If I had an object which implemented IList<T> and ICloneable, and T was a class type, I would expect that cloning should give me a new list which held references to the same object instances as the original. I would regard a clone function that did anything else as broken. For an object to clone deeper than its semantic contents is not "optional"--it is a breaking behavior.Dowie
@AndreyShchekin: I suppose perhaps the biggest problem is that Microsoft didn't simply specify that ICloneable should work in the only fashion that would be logical and useful in the general case (which is what I described), but I can't see any reason why it should be expected to work in any other way. Of course, that would hardly be the only case where Microsoft has been less than optimal in its semantic specifications. Object equality has the same issue. It's not hard to define an equivalence relation which would be unambiguously applicable to every single type of object, but...Dowie
...Microsoft put a few quirks in a couple types' implementation of Equals. A proper equivalence relation would specify that two objects should compare "equal" if and only if they would be semantically indistinguishable except by checking reference equality. Having such an equivalence relation available for all classes would facilitate things like interning classes (given two objects compare equal, one of which is in an interning dictionary, one would be able to substitute the other safely). Too bad the Object.Equals overrides of float, double, and Decimal don't work that way.Dowie
FWIW I came to this question in search of a canonical method to clone a list such that I could alter properties of its members without altering the members in the original list.Maggs
Lots of valid reasons why it's a bad idea. However, in the real world, you sometimes need it. For example, in .net Workflow Foundation, I need work with an object, modify it's content deep in some library code, and then give it back to WF. If it's the same reference, it won't detect changes :( so I have to make a copy of it, and a shallow copy is enough.Polypody
L
19

I need to ask, what exactly would you do with the interface other than implement it? Interfaces are typically only useful when you cast to it (ie does this class support 'IBar'), or have parameters or setters that take it (ie i take an 'IBar'). With ICloneable - we went through the entire Framework and failed to find a single usage anywhere that was something other than an implementation of it. We've also failed to find any usage in the 'real world' that also does something other than implement it (in the ~60,000 apps that we have access to).

Now if you would just like to enforce a pattern that you want your 'cloneable' objects to implement, that's a completely fine usage - and go ahead. You can also decide on exactly what "cloning" means to you (ie deep or shallow). However, in that case, there's no need for us (the BCL) to define it. We only define abstractions in the BCL when there is a need to exchange instances typed as that abstraction between unrelated libraries.

David Kean (BCL Team)

Limiting answered 25/2, 2012 at 2:3 Comment(13)
Non-generic ICloneable isn't very useful, but ICloneable<out T> could be quite useful if it inherited from ISelf<out T>, with a single method Self of type T. One doesn't often need "something that's cloneable", but one may very well need a T that's cloneable. If a cloneable object implements ISelf<itsOwnType>, a routine that needs a T that's cloneable can accept a parameter of type ICloneable<T>, even if not all of the cloneable derivatives of T share a common ancestor.Dowie
Thanks. That's similar to cast situation I mentioned above (ie 'does this class support 'IBar'). Sadly, we've only come up with very limited and isolated scenarios where you would actually make use of the fact that T is cloneable. Do you have situations in mind?Limiting
One thing that's sometimes awkward in .net is managing mutable collections when the contents of a collection are supposed to be viewed as a value (i.e. I will want to know later the set of items that are in the collection now). Something like ICloneable<T> could be useful for that, though a broader framework for maintaining parallel mutable and immutable classes might be more helpful. In other words, code which needs to see what some type of Foo contains but is neither going to mutate it nor expect that it won't ever change could use an IReadableFoo, while...Dowie
...code which wants to hold the contents of Foo could use an ImmutableFoo while code that to wants to manipulate it could use a MutableFoo. Code given any type of IReadableFoo should be able to get either a mutable or immutable version. Such a framework would be nice, but unfortunately I can't find any nice way to set things up in a generic fashion. If there were a consistent way to make a read-only wrapper for a class, such a thing could be used in combination with ICloneable<T> to make an immutable copy of a class which holds T'.Dowie
@Dowie If you're wanting to clone a List<T>, such that the cloned List<T> is a new collection holding pointers to all of the same objects in the original collection, there are two easy ways of doing that without ICloneable<T>. The first is the Enumerable.ToList() extension method: List<foo> clone = original.ToList(); The second is the List<T> constructor that takes an IEnumerable<T>: List<foo> clone = new List<foo>(original); I suspect the extension method is probably just calling the constructor, but both of these will do what you're requesting. ;)Woody
@CptRobby: Given any IEnumerable<T>, one can produce an immutable sequence from it by calling ToList and then wrapping that in a ReadOnlyCollection<T> or otherwise ensuring it will never be exposed to anything that might mutate it. My complaints with that are (1) a List<T> is just one kind of thing; there's no standard interface which would allow one to make an immutable copy of anything that implements the interface without having to know what it is; (2) "cloning" an instance which is already immutable by simply copying the reference can be many orders of magnitude more efficient...Dowie
...than making a copy of its contents. Suppose, for example, that one has an IEnumerable<T> mySequence, and one wants wasSequence` to refer to an object that will always return the sequence of items that mySequence returns right now. If mySequence is Enumerable.Range(1,1000000), one could say wasSequence=new ReadOnlyCollection<int>(mySequence.ToList()); but copying the reference would be many orders of magnitude faster.Dowie
@supercat: I'm not disagreeing with you per se, but would like to point out that ReadOnlyCollection<T> can only be constructed from an IList<T>, so unless you want to use some other immutable collection wrapper, you're pretty much stuck with converting it to a List<T> (or an array) first. I agree that it wouldn't make sense to clone an immutable collection, but again, I only know of ReadOnlyCollection<T>, so it would be trivial to provide an explicit exclusion for that case. As for Enumerable.Range(), I had to look it up to see what it is, but...Woody
...from the description, it returns some unknown type that defers execution until it is enumerated. So the only thing we can say about it is that you can enumerate it because it implements IEnumerable<int>, so there's no way of knowing whether it is mutable or not without trying to cast it to an IList<int> or something like that and trying to change it. So the only thing you can do in that situation is to enumerate it into a list or array and create a ReadOnlyCollection<int> from it, if you are wanting to ensure that you have an immutable collection.Woody
Oh, and from just doing a quick search, it appears that there are a few other less used immutable collections SynchronizedReadOnlyCollection<T>, ReadOnlyObservableCollection<T>, and ReadOnlyDictionary<TKey,TValue>. And it looks like BCL is working on adding more (hopefully they will all implement IReadOnlyCollection<T> to easily type check them). I've only used ReadOnlyCollection<T> on one occasion, so I don't claim to be an expert in that area. And again, I'm not debating you, I was just pointing out a simple solution to the scenario you pointed out. ;)Woody
@CptRobby: My point was that the lack of standardized interfaces related to immutability means that there's often no alternative to making redundant copies of immutable data--a fact which can increase execution size and memory requirements by orders of magnitude. The indicated Enumerable.Range, btw, is an immutable object which encapsulates the sequence {1, 2, 3, 4,..., 9999999, 10000000}. Given that sequence, one could make an immutable object that encapsulates the same sequence by building a 10,000,000-item array but simply copying the reference is a lot cheaper.Dowie
@CptRobby: Mr. Kean wondered what use cloning would be, and I was trying to suggest that cloning in and of itself isn't necessarily useful, but it could be useful if there were also some support for immutability. If there existed an IConvertibleToImmutable with an AsImmutable, then types with immutable or partially-immutable backing stores could implement it in a manner more efficient than a straight-up clone.Dowie
@supercat: Again, I'm no expert on immutable collections or objects, but if I were using them, I would agree that it would be better to have some sort of flag on them to specify that they are immutable. Unfortunately, it appears that even IReadOnlyCollection<T> fails in that regard since it is implemented on List<T>! [facepalm] Regardless, all I was suggesting was a way to clone a list, without even mentioning mutability. Thanks for the discussion though. ;)Woody
C
14

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();
}
Confectionary answered 11/2, 2009 at 11:26 Comment(4)
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.Dowie
@Dowie why not make it an answer then?Calibre
"There is a lot of interfaces/classes/etc... which is very usefull, but is not part of .NET Frameworku base library." Can you give us examples?Hasp
@Hasp i.e.: advanced datastructures like b-tree, red-black tree, circle buffer. In '09 there was no tuples, generic weak-reference, concurrent collections ...Confectionary
A
12

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();
}
Ade answered 11/2, 2009 at 11:19 Comment(5)
I included a snippet since because of the honor of the copy-rights the link was broken...Cyzicus
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.Dowie
@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.Ade
@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.Dowie
@Mauricio Scheffer: BTW, I would suggest that ICloneable(of T) also support a readonly Self property (of return type T). This would allow a method to accept an ICloneable(of Foo) and use it (via the Self property) as a Foo.Dowie
S
11

Having read recently the article Why Copying an Object is a terrible thing to do?, I think this question needs additional clafirication. Other answers here provide good advices, but still the answer isn't complete - why no ICloneable<T>?

  1. Usage

    So, you have a class that implements it. While previously you had a method that wanted ICloneable, it now has to be generic to accept ICloneable<T>. You would need to edit it.

    Then, you could have got a method that checks if an object is ICloneable. What now? You can't do is ICloneable<> and as you don't know the type of the object at compile-type, you can't make the method generic. First real problem.

    So you need to have both ICloneable<T> and ICloneable, the former implementing the latter. Thus an implementer would need to implement both methods - object Clone() and T Clone(). No, thanks, we already have enough fun with IEnumerable.

    As already pointed out, there is also the complexity of inheritance. While covariance may seem to solve this problem, a derived type needs to implement ICloneable<T> of its own type, but there is already a method with the same signature (= parameters, basically) - the Clone() of the base class. Making your new clone method interface explicit is pointless, you will lose the advantage you sought when creating ICloneable<T>. So add the new keyword. But don't forget that you would also need to override the base class' Clone() (the implementation has to remain uniform for all derived classes, i.e. to return the same object from every clone method, so the base clone method has to be virtual)! But, unfortunately, you can't both override and new methods with the same signature. Choosing the first keyword, you'd lose the goal you wanted to have when adding ICloneable<T>. Chossing the second one, you'd break the interface itself, making methods that should do the same return different objects.

  2. Point

    You want ICloneable<T> for comfort, but comfort is not what interfaces are designed for, their meaning is (in general OOP) to unify the behavior of objects (although in C#, it is limited to unifying the outer behavior, e.g. the methods and properties, not their workings).

    If the first reason hasn't convinced you yet, you could object that ICloneable<T> could also work restrictively, to limit the type returned from the clone method. However, nasty programmer can implement ICloneable<T> where T is not the type that is implementing it. So, to achieve your restriction, you can add a nice constraint to the generic parameter:
    public interface ICloneable<T> : ICloneable where T : ICloneable<T>
    Certainly more restrictive that the one without where, you still can't restrict that T is the type that is implementing the interface (you can derive from ICloneable<T> of different type that implements it).

    You see, even this purpose couldn't be achieved (the original ICloneable also fails at this, no interface can truly limit the behavior of the implementing class).

As you can see, this proves making the generic interface is both hard to fully implement and also really unneeded and useless.

But back to the question, what you really seek is to have comfort when cloning an object. There are two ways to do it:

Additional methods

public class Base : ICloneable
{
    public Base Clone()
    {
        return this.CloneImpl() as Base;
    }

    object ICloneable.Clone()
    {
        return this.CloneImpl();
    }

    protected virtual object CloneImpl()
    {
        return new Base();
    }
}

public class Derived : Base
{
    public new Derived Clone()
    {
        return this.CloneImpl() as Derived;
    }

    protected override object CloneImpl()
    {
        return new Derived();
    }
}

This solution provides both comfort and intended behavior to users, but it's also too long to implement. If we didn't want to have the "comfortable" method returning the current type, it is much more easy to have just public virtual object Clone().

So let's see the "ultimate" solution - what in C# is really intented to give us comfort?

Extension methods!

public class Base : ICloneable
{
    public virtual object Clone()
    {
        return new Base();
    }
}

public class Derived : Base
{
    public override object Clone()
    {
        return new Derived();
    }
}

public static T Copy<T>(this T obj) where T : class, ICloneable
{
    return obj.Clone() as T;
}

It's named Copy not to collide with the current Clone methods (compiler prefers the type's own declared methods over extension ones). The class constraint is there for speed (doesn't require null check etc.).

I hope this clarifies the reason why not to make ICloneable<T>. However, it is recommended not to implement ICloneable at all.

Strangeness answered 12/11, 2014 at 19:29 Comment(2)
Appendix #1: The only possible usage of a generic ICloneable is for value types, where it could circumvent the boxing of the Clone method, and it implies you have the value unboxed. And as structures can be cloned (shallowly) automatically, there is no need to implement it (unless you make it specific that it means deep copy).Strangeness
The extension method is genious!Roulade
J
1

A big problem is that they could not restrict T to be the same class. Fore example what would prevent you from doing this:

interface IClonable<T>
{
    T Clone();
}

class Dog : IClonable<JackRabbit>
{
    //not what you would expect, but possible
    JackRabbit Clone()
    {
        return new JackRabbit();
    }

}

They need a parameter restriction like:

interfact IClonable<T> where T : implementing_type
Junket answered 19/7, 2012 at 22:4 Comment(5)
That doesn't seem as bad as class A : ICloneable { public object Clone() { return 1; } /* I can return whatever I want */ }Zaccaria
I don't really see what the problem is. No interface can force implementations to behave reasonably. Even if ICloneable<T> could constrain T to match its own type, that wouldn't force an implementation of Clone() to return anything remotely resembling the object upon which it was cloned. Further, I would suggest that if one is using interface covariance, it may be best to have classes which implement ICloneable be sealed, have the interface ICloneable<out T> include a Self property which is expected to return itself, and...Dowie
...have consumers cast or constrain to ICloneable<BaseType> or ICloneable<ICloneable<BaseType>>. The BaseType in question should have a protected method for cloning, which would be called by the type which implements ICloneable. This design would allow for the possibility that one might wish to have a Container, a CloneableContainer, a FancyContainer, and a CloneableFancyContainer, the latter being usable in code which requires a cloneable derivative of Container or which requires a FancyContainer (but doesn't care if it's cloneable).Dowie
The reason I'd favor such a design is that the issue of whether a type can be cloned sensibly is somewhat orthogonal to other aspects of it. For example, one might have a FancyList type which could be cloned sensibly, but a derivative might automatically persist its state in a disk file (specified in the constructor). The derived type couldn't be cloned, because its state would be attached to that of a mutable singleton (the file), but that shouldn't preclude use of the derived type in places which need most features of a FancyList but wouldn't need to clone it.Dowie
+1 - this answer is the only one from those I've seen here that really answers the question.Strangeness
W
0

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.

Wilkinson answered 11/2, 2009 at 11:19 Comment(3)
"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.Barbarize
Dennis: See Marc's answers. Covariance / inheritance issues make this interface pretty much unusable for any scenario that is at least marginally complicated.Streusel
Yea, i can see the limitations of ICloneable, for sure... I rarely need to use cloning, though.Wilkinson

© 2022 - 2024 — McMap. All rights reserved.