Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c#

icloneable

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?

like image 258
sapbucket Avatar asked Aug 02 '11 18:08

sapbucket


People also ask

Which of the following method should be implemented when ICloneable interface is used?

ICloneable interface it implements Clone() method which performs a deep copy operation on objects of that class.

What is ICloneable interface in C#?

The ICloneable interface enables you to provide a customized implementation that creates a copy of an existing object. The ICloneable interface contains one member, the Clone method, which is intended to provide cloning support beyond that supplied by Object. MemberwiseClone.


3 Answers

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();
}
like image 66
David Morton Avatar answered Oct 17 '22 22:10

David Morton


The problem is your generic constraint where T : IClonable<T>. Because you're "instantiating" your interface as ICloneable<List<Instance>>, List<Instance> is your T, and so the generic constraint translates to where List<Instance> : IClonable<List<Instance>>. List<Instance> does not fulfill that constraint.

Perhaps you're trying to do something like this:

public interface ICloneableList<T> : ICloneable where T : ICloneable
{
}
like image 30
Jacob Avatar answered Oct 17 '22 23:10

Jacob


To add to the other good answers already there - when you clone, you expect to get an identical copy back, right? So instead of:

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

Shouldn't it actually be:

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

That way you will also get no compiler errors.

like image 1
Vilx- Avatar answered Oct 17 '22 23:10

Vilx-