2

I am having trouble making an extension method for cloning a list.

This answer is very close, providing the following extension method :

static class Extensions
{
    public static IList<T> Clone<T>(this IList<T> listToClone) where T: ICloneable
    {
        return listToClone.Select(item => (T)item.Clone()).ToList();
    }
}

The problem here is that ToList() returns an object of type List which isn't necessarily what I used to call the function with. I would like it to return an object of the same type as the one I called Clone on.

The closest I've been able to come is this :

static private object Clone<T, K>(this T container)
    where T : IList<K>, new()
    where K : ICloneable
{
    T res = new T();

    int size = container.Count;
    for (int i = 0; i < size; ++i)
    {
        K obj = container[i];
        res.Add((K)obj.Clone());
    }

    return res;
}

but I keep getting a CS1061 error (does not contain a definition for 'Clone' and no extension method 'Clone' accepting a first argument of type [...] could be found...). Unless I specify :

List<CloneableType> clone = myList.Clone<List<CloneableType>, CloneableType>();

Is there a way to simply call Clone() without the specifications? Why can't the compiler deduce the types I want?

Note : Although the example uses the generic List, it's important for me that this works for non List implementations of IList as well.

Community
  • 1
  • 1
Godfather
  • 1,089
  • 9
  • 21
  • Why are you using generic method, when deep cloning should be implemented in the List class itself?? – Euphoric Apr 16 '14 at 06:44
  • I don't get the difference. Your first method is declared to return `IList`, `List` implements `IList`. What's the problem? – MarcinJuraszek Apr 16 '14 at 06:54
  • 1
    See this: [Generics: Why can't the compiler infer the type arguments in this case?](http://stackoverflow.com/questions/3968834/generics-why-cant-the-compiler-infer-the-type-arguments-in-this-case) The crucial point in Eric Lippert's answer is: "The fundamental rule that you're missing is probably that constraints are not part of the signature. Type inference works off of the signature." – Ani Apr 16 '14 at 06:55
  • @Ani, that clears a lot of things up for me, thanks. – Godfather Apr 16 '14 at 15:57

1 Answers1

3

The C# compiler infers generic types from parameters, but there is no parameter of type K in your method.

As an alternative, you could use the following method:

static class Extensions
{
    public static IList<T> Clone<T>(this IList<T> listToClone)
         where T : ICloneable
    {
        var list = (IList<T>)Activator.CreateInstance(listToClone.GetType());
        foreach (var item in listToClone)
        {
            list.Add((T)item.Clone());
        }
        return list;
    }
}

Note that:

  • This could fail at runtime (the list type doesn't have a public parameterless constructor - somewhat unlikely)
  • You'd still to cast the result to the original type

You might want to consider using serialization as an alternative method for deep-cloning objects.

Eli Arbel
  • 22,391
  • 3
  • 45
  • 71
  • This ended up being pretty much what I went for. I'm ok with casting the result since I wanted the behavior to mimic that of the ICloneable interface. Thanks! – Godfather Apr 16 '14 at 15:52