views:

156

answers:

2

HashSet does not have an AddRange method, so I want to write an extension method for it. This is what I have:

public static void AddRange<T>(this ICollection<T> collection, IEnumerable<T> list)
{
    foreach (var item in list)
    {
        collection.Add(item);
    }
}

I have a base class, Media, and a derived class, Photo. This is the code that I want to work:

var photos = new List<Photo>();
var media = new HashSet<Media>();
media.AddRange(photos);

However, the compiler is telling me that it can't convert the List<Photo> to IEnumerable<Media> when I try to use AddRange(). I'm pretty sure this is because I have IEnumerable<T> in the extension method, but how do I write it so that the type is different than the type in AddRange<T>?

+3  A: 

Yet another generic-variance issue. You will need to call the Cast<Media> extension method to convert the IEnumerable<Photo> to IEnumerable<Media> before it will be acceptable to your AddRange implementation's signature.

Alternatively, you can declare

AddRange<T, U>(ICollection<T>, IEnumerable<U>) where U : T

as a workaround to produce the correct variance in the signature.

Jeffrey Hantin
When you say `Cast<Media>`, are you saying to write it myself or is it something that's included with the .NET Framework?
Daniel T.
That's in the .NET framework in the System.Linq namespace as an extension method on IEnumerable<T>.
mquander
Oh nevermind, I just found it. I was trying to use Cast<Media>(Blahblah) instead of Blahblah.Cast<Media>.
Daniel T.
+3  A: 

Actually, HashSet<T> may not have an AddRange method, but it has a UnionWith method, which is semantically what you are looking for. The difference in name reflects the fact that it filters out duplicates. It also has a constructor that takes an IEnumerable<T> as a parameter, if you just want to copy a collection into a HashSet.

In the general case, the above answer recommending to use a generic constraint ("where T : U") is correct.

mquander
Nice, I didn't know that. Combined with Jeffrey's `Cast<Media>` answer, this means I can get rid of my extension method!
Daniel T.
Hmm, handy to know.
R. Bemrose
Just an additional note: the actual method is `Union`, not `UnionWith`, and it returns an IEnumerable<T>. I had to use the extension method after all because I needed to add to the HashSet.
Daniel T.
There's an IEnumerable<T> extension method called Union that you can use to take the union of any two sequences, but if you've got a HashSet<T> already, it has a separate method, UnionWith, that will be more efficient, since it extends your existing set instead of allocating an entirely new one.
mquander
Reference: http://msdn.microsoft.com/en-us/library/bb342097.aspx
mquander