Search code examples
c#icloneable

How to use ICloneable<T> when T is List<T>?


I have the following:

    public class InstanceList : List<Instance> {}

I would like to make this cloneable. Following the example here: Why no ICloneable<T>?

I tried the following:

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

    public class InstanceList : List<Instance>, ICloneable<List<Instance>>  {}

But I get a compiler error. The error message states that List<Instance> must be convertible to ICloneable<List<Instance>> in order to use parameter T in the generic interface ICloneable<T>.

What am I missing here?


Solution

  • You can't do this, because you can't define List<T> yourself. You would only be able to do this if you could declare your own List<T> because of the way you've constrained ICloneable<T>. Since List<T> truly doesn't implement ICloneable<T>, you're going to have to have the type of T be InstanceList instead, which you do have control over.

    Here's how you would implement it:

    public class InstanceList : List<Instance>, ICloneable<InstanceList>
    {
        public InstanceList Clone()
        {
            // Implement cloning guts here.
        }
    
        object ICloneable.Clone()
        {
            return ((ICloneable<InstanceList>) this).Clone();
        }
    }
    
    public class Instance
    {
    
    }
    
    public interface ICloneable<T> : ICloneable where T : ICloneable<T>
    {
        new T Clone();
    }
    

    Of course, there is another alternative you could do. You could widen your generics a little bit, to create a CloneableList<T> type:

    public class CloneableList<T> : List<T>, ICloneable<CloneableList<T>>
    {
        public CloneableList<T> Clone()
        {
            throw new InvalidOperationException();
        }
    
        object ICloneable.Clone()
        {
            return ((ICloneable<CloneableList<T>>) this).Clone();
        }
    }
    
    public interface ICloneable<T> : ICloneable where T : ICloneable<T>
    {
        new T Clone();
    }
    

    And if you really want to get fancy, create something that restricts T to ICloneable. Then you could implement ICloneable on the Instance class, and anything else you want to include in an ICloneable<T> list, thus treating every CloneableList<T> in the exact same way, avoiding a different implementation of ICloneable<T> for each and every cloneable list you want to create.

    public class CloneableList<T> : List<T>, ICloneable<CloneableList<T>> where T : ICloneable
    {
        public CloneableList<T> Clone()
        {
            var result = new CloneableList<T>();
            result.AddRange(this.Select(item => (T) item.Clone()));
            return result;
        }
    
        object ICloneable.Clone()
        {
            return ((ICloneable<CloneableList<T>>) this).Clone();
        }
    }
    
    public interface ICloneable<T> : ICloneable where T : ICloneable<T>
    {
        new T Clone();
    }