Reputation: 2922
I learned that HashSet
implements the IEnumerable
interface. Thus, it is possible to implicitly cast a HashSet
object into IEnumerable
:
HashSet<T> foo = new HashSet<T>();
IEnumerable<T> foo2 = foo; // Implicit cast, everything fine.
This works for nested generic types, too:
HashSet<HashSet<T>> dong = new HashSet<HashSet<T>>();
IEnumerable<IEnumerable<T>> dong2 = dong; // Implicit cast, everything fine.
At least that's what I thought. But if I make a Dictionary
, I run into a problem:
IDictionary<T, HashSet<T>> bar = new Dictionary<T, HashSet<T>>();
IDictionary<T, IEnumerable<T>> bar2 = bar; // compile error
The last line gives me the following compile error (Visual Studio 2015):
Cannot implicitly convert type
System.Collections.Generic.IDictionary<T, System.Collections.Generic.HashSet<T>>
toSystem.Collections.Generic.IDictionary<T, System.Collections.Generic.IEnumerable<T>>
.An explicit conversion exists (are you missing a cast?)
But if I do the cast by writing
IDictionary<T, IEnumerable<T>> bar2 = (IDictionary<T, IEnumerable<T>>) bar;
then I get an invalid cast exception at runtime.
Two questions:
HashSet
does implement the IEnumerable
interface?Upvotes: 3
Views: 1159
Reputation: 26233
The reason it doesn't work is that the value in IDictionary<TKey, TValue>
is not co-variant (and nor is the key, for the same reasons). If it were allowed to be, then this code would compile, but has to result in an exception:
IDictionary<T, HashSet<T>> foo = new Dictionary<T, HashSet<T>>();
IDictionary<T, IEnumerable<T>> bar = foo;
foo.Add(key, new List<T>());
You'd think adding a List<T>
would work, as it would compile given the value type is supposedly IEnumerable<T>
. It can't succeed, though, as the actual value type is HashSet<T>
.
So, yes: the only way is to create a new dictionary.
var bar = foo.ToDictionary(x => x.Key, x => x.Value.AsEnumerable());
Upvotes: 5
Reputation: 10871
How do I solve this? Is the only way to iterate over the keys and build up a new dictionary bit by bit?
It may not be the most elegant solution, but it works:
IDictionary<T, HashSet<T>> bar = new Dictionary<T, HashSet<T>>();
IDictionary<T, IEnumerable<T>> bar2 = bar.ToDictionary(x => x.Key, y => (IEnumerable<T>)y.Value);
The reason why the Dictionary cast isn't working is because IEnumerable
is co-variant, note <out T>
in the declaration
public interface IEnumerable<out T> : IEnumerable
IDictionary
isn't.
public interface IDictionary<TKey, TValue> : ICollection<KeyValuePair<TKey, TValue>>, IEnumerable<KeyValuePair<TKey, TValue>>, IEnumerable
You can read more about it here: https://msdn.microsoft.com/en-us/library/dd799517(v=vs.110).aspx
Upvotes: 3