20

See the following code snippet:

(IEnumerable<object>)new Dictionary<string, string>()

The above cast will throw an invalid cast exception.

Actually, IDictionary<TKey, TValue> also indirectly implements IEnumerable<out T>, because it also implements ICollection<T>. That is, the whole cast should be valid.

In fact, for me it is even more strange that if I run the whole cast on a debugger watch slot, it works!

Enter image description here

What's going on?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Matías Fidemraizer
  • 63,804
  • 18
  • 124
  • 206
  • 1
    @PatrickHofman Why it doesn't? There's only one interface called `IEnumerable` which has a covariant type parameter. Even when covariance won't work with value types.............Am I mistaken? – Matías Fidemraizer Jul 18 '16 at 09:43
  • @CodeCaster I mean that the interface itself is `IEnumerable` – Matías Fidemraizer Jul 18 '16 at 09:59
  • @CodeCaster Argh, https://msdn.microsoft.com/en-us/library/9eekhta0(v=vs.110).aspx – Matías Fidemraizer Jul 18 '16 at 10:00
  • @CodeCaster You know what we're talking about. I know that `IEnumerable` isn't the same type as `IEnumerable` but when you implement these, you're implementing `IEnumerable` – Matías Fidemraizer Jul 18 '16 at 10:03
  • Great, that clears that up then. I got tired clicking through the inheritance tree on MSDN. ;) – CodeCaster Jul 18 '16 at 10:17
  • **Why this works in the Watch window** - From [Eric Lippert's article](https://blogs.msdn.microsoft.com/ericlippert/2009/03/19/representation-and-identity/), when the compiler sees following code: `short s; var o = (object)s; var i = (int)o;` it cannot determine that what is actually in `o` is a `short`, and the only way for this to work would be to spin up at runtime a small instance of the compiler to find the appropriate conversion method. My understanding is the debugging features of VS already are using their own instance of the compiler, so there is no benefit in this limitation. – Zev Spitz Jul 18 '16 at 12:14

4 Answers4

25

That dictionary does implement IEnumerable<KeyValuePair<TKey, TValue>> and IEnumerable, but IEnumerable of a struct is not the same as IEnumerable of an object. Variance only works for reference-types. KeyValuePair<K, V> is a struct and not a class.

This does work at my end and should be the logical choice:

var x = (IEnumerable)new Dictionary<string, string>();

As a sample, this does work:

List<string> l = new List<string>();
var x = (IEnumerable<object>)l;

But this one doesn't:

List<DateTime> l2 = new List<DateTime>();
var x = (IEnumerable<object>)l2;

Clearly indicating the struct is the problem.

(Why it works in your Watch windows, I don't know)

StayOnTarget
  • 11,743
  • 10
  • 52
  • 81
Patrick Hofman
  • 153,850
  • 22
  • 249
  • 325
4

I think this is because KeyValuePair is a Value-Type.

This would fail:

List<int> ints = new List<int>();
var objs = (IEnumerable<object>)ints;

This would work:

List<string> ints = new List<string>();
var objs = (IEnumerable<object>)ints;

same goes for the dictionary.

Zein Makki
  • 29,485
  • 6
  • 52
  • 63
  • You're absolutely right. For now we should accept Patrick's answer, but it would be great that someone like Eric Lippert could answer us why the debugger has more power than us! – Matías Fidemraizer Jul 18 '16 at 09:48
  • I guess that is just a parse bug in the debugging tools. I don't think they really compile the statement on the fly and evaluate it then. @MatíasFidemraizer – Patrick Hofman Jul 18 '16 at 09:51
  • 1
    @PatrickHofman Yeah, it should be that... BTW it could be considered as a bug: debugger isn't 100% coherent with actual runtime behavior :\ – Matías Fidemraizer Jul 18 '16 at 09:53
  • @MatíasFidemraizer On one hand, you are right. On the other... it's still quite impressive how close the debugger gets, when you realize that it can't use the .NET framework at all (the process you are debugging is suspended, and that includes the whole .NET runtime for that process). On a LISP machine, "debugging" still left you every faculty of the "runtime"; the same is true when doing native debugging (and yet, native debuggers usually suck compared to .NET's); .NET debugger can't do either. I hope they'll fix every tiny bug like this, but can't really hold it against them :D – Luaan Jul 18 '16 at 11:15
  • @Luaan You're right. After all, software is done by people like us and we can't cover every corner case in the world. – Matías Fidemraizer Jul 18 '16 at 12:14
1

If you really can't live with plain IEnumerable, try this:

new Dictionary<string, string>().Cast<object>();
Paulo Morgado
  • 14,111
  • 3
  • 31
  • 59
0

This is strange. Your code snippet works fine with .NET 4.0. I suggest you cast to IEnumerable.

Read: IDictionary Interface (MSDN)

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131