3

I understand the difference between a deep and shallow clone of an object, but according to Simon's answer on this (copy-constructor-versus-clone) question, a generic and non-generic version should be supplied. Why?

You can define two interfaces, one with a generic parameter to support strongly typed cloning and one without to keep the weakly typed cloning ability for when you are working with collections of different types of cloneable objects:

I mean it's trivial enough to make the different interfaces, but in the generic-heavy paradigm of modern C#, I am finding it difficult to come up with a valid reason why you would ever want to use the non-generic and weakly-typed version. Heck, you can even have T:object and do the same thing!

I would write my interfaces like this:

public interface IShallowCloneable
{
    object Clone();
}

public interface IShallowCloneable<T> // Should this derive IShallowCloneable?
{
    T Clone();
}

public interface IDeepCloneable
{
    object Clone();
}

public interface IDeepCloneable<T> // Should this derive IDeepCloneable?
{
    T Clone();
}

And my class would implement it like this:

public class FooClass : IDeepCloneable<FooClass>
{
    // Implementation
}
Community
  • 1
  • 1
Kyle Baran
  • 1,793
  • 2
  • 15
  • 30
  • You could also consider making the template interface covariant on the type: see: http://msdn.microsoft.com/en-us/library/dd997386.aspx. Therefore you need to marke the template argument as `out T` like `IShallowCloneable` – Onur Jun 26 '14 at 09:22

2 Answers2

5

As that quote says, if you want to be able to work with collections of generic types produced from the same generic definition with different type parameters you really have to provide a non-generic interface.

Consider a list of IDeepCloneable<Widget> and IDeepCloneable<Gadget> instances. You can always make it a List<object>, but then you can't clone them unless you resort to runtime type checks.

If you want to be able to clone (or in general, access in some other manner) these items polymorphically you need them to be typed such that their static type offers the minimum public interface that will let you do the job. In this case, that would make our list a List<IDeepCloneable>.

If you don't intend to use these interfaces polymorphically then having a non-generic IDeepCloneable doesn't offer anything.

Jon
  • 428,835
  • 81
  • 738
  • 806
  • Oh! Now I see, so you'd use the non-generic version when you want to mix different fundamental types together... which is what interfaces were designed to do, ha. – Kyle Baran Jun 26 '14 at 09:23
1

You answered your own question. :)

You need the non-generic version for working with non generic collections.

Take for example the IEnumerable and IEnumerable<T> interfaces. The generic interface implements the non-generic one, but only to provide backwards compatibility with non-generic collections:

Ideally all of the generic collection interfaces (e.g. ICollection<T>, IList<T>) would inherit from their non-generic counterparts, such that the generic interface instances could be used with both generic and non-generic code. For example, it would be convenient if an IList<T> could be passed to code that expects an IList.

Jernej Gorički
  • 894
  • 1
  • 7
  • 16