Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# type conversion: Explicit cast exists but throws a conversion error?

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>> to System.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:

  • How do I solve this? Is the only way to iterate over the keys and build up a new dictionary bit by bit?
  • Why do I get this problem in the first place, even thoughHashSet does implement the IEnumerable interface?
like image 311
Kjara Avatar asked Jul 17 '26 17:07

Kjara


1 Answers

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());
like image 70
Charles Mager Avatar answered Jul 20 '26 06:07

Charles Mager



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!