There actually are three possible ways to do this:
public static ICollection<T> CreateCopyReflection<T> (this ICollection<T> c)
{
var n = (ICollection<T>) Activator.CreateInstance (c.GetType());
foreach (var item in c)
n.Add (item);
return n;
}
public static IEnumerable<T> CreateCopyLinq<T> (this IEnumerable<T> c) => c.Select (arg => arg);
public static IEnumerable<T> CreateCopyEnumeration<T> (this IEnumerable<T> c)
{
foreach (var item in c)
yield return item;
}
Note that we can use IEnumerables here without worrying, as ICollection<T>
derives from IEnumerable<T>
.
The first solution creates a copy using reflection, the second one using Linq and the third one using enumeration. We can now profile this with the following code:
var myList = Enumerable.Range (0, 100000000).ToList();
var trueCopy = new List<int> (myList);
var time = Environment.TickCount;
var copyOne = myList.CreateCopyReflection();
Console.WriteLine($"Refelection copy: {Environment.TickCount - time}");
time = Environment.TickCount;
var copyTwo = myList.CreateCopyLinq ();
Console.WriteLine ($"Linq copy: {Environment.TickCount - time}");
time = Environment.TickCount;
var copyThree = myList.CreateCopyEnumeration ();
Console.WriteLine ($"Enumeration copy: {Environment.TickCount - time}");
time = Environment.TickCount;
Which results in:
Reflection copy: 1375
Linq copy: 0
Enumeration copy: 0
However, we have to keep in mind that c# is lazy here, which means that it didn't actually calculate the values, so we only get comparable results when enumerating the IEnumerables:
var myList = Enumerable.Range (0, 100000000).ToList();
var trueCopy = new List<int> (myList);
var time = Environment.TickCount;
var copyOne = myList.CreateCopyReflection().ToList();
Console.WriteLine($"Reflection copy: {Environment.TickCount - time}");
time = Environment.TickCount;
var copyTwo = myList.CreateCopyLinq ().ToList();
Console.WriteLine ($"Linq copy: {Environment.TickCount - time}");
time = Environment.TickCount;
var copyThree = myList.CreateCopyEnumeration ().ToList();
Console.WriteLine ($"Enumeration copy: {Environment.TickCount - time}");
time = Environment.TickCount;
Which results in:
Reflection copy: 1500
Linq copy: 1625
Enumeration copy: 3140
So we can see that the enumeration is the slowest, followed by linq and then reflection. However, reflection and linq are very close and linq has the huge (at least in a lot of cases) advantage that it's lazy (as well as the enumeration), which is why I'd use it.
It's quite interesting to compare to if cascades:
private static void Main ()
{
var myList = Enumerable.Range (0, 100000000).ToList();
var trueCopy = new List<int> (myList);
var time = Environment.TickCount;
var copyOne = myList.CreateCopyReflection().ToList();
Console.WriteLine($"Reflection copy: {Environment.TickCount - time}");
time = Environment.TickCount;
var copyTwo = myList.CreateCopyLinq ().ToList();
Console.WriteLine ($"Linq copy: {Environment.TickCount - time}");
time = Environment.TickCount;
var copyThree = myList.CreateCopyEnumeration ().ToList();
Console.WriteLine ($"Enumeration copy: {Environment.TickCount - time}");
time = Environment.TickCount;
var copyFour = myList.CreateCopyCascade ();
Console.WriteLine($"Cascade copy: {Environment.TickCount - time}");
time = Environment.TickCount;
Console.ReadLine ();
}
public static ICollection<T> CreateCopyReflection<T> (this ICollection<T> c)
{
var n = (ICollection<T>) Activator.CreateInstance (c.GetType());
foreach (var item in c)
n.Add (item);
return n;
}
public static IEnumerable<T> CreateCopyLinq<T> (this IEnumerable<T> c) => c.Select (arg => arg);
public static IEnumerable<T> CreateCopyEnumeration<T> (this IEnumerable<T> c)
{
foreach (var item in c)
yield return item;
}
public static ICollection<T> CreateCopyCascade<T> (this ICollection<T> c)
{
if (c.GetType() == typeof(List<T>))
return new List<T> (c);
if (c.GetType() == typeof(HashSet<T>))
return new HashSet<T> (c);
//...
return null;
}
Which results in:
Reflection copy: 1594
Linq copy: 1750
Enumeration copy: 3141
Cascade copy: 172
So we can see that the cascade is way faster - however, it won't work if other collections are created which derive from ICollection, as it won't know them, so this solution isn't very advisable.