Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# explicit cast - from collection of KeyValuerPair to Dictionary

I have a list of KeyValuePairs. I normally would use ToDictionary.

However I just noted that the error message (shown below) has something about explicit cast, which implies I can actually cast the list to Dictionary<...>. How can I do this?

Cannot implicitly convert type 'System.Linq.IOrderedEnumerable<System.Collections.Generic.KeyValuePair<int,string>>' to 'System.Collections.Generic.Dictionary<int, string>'. An explicit conversion exists (are you missing a cast?)

Sample code:

Dictionary<int, string> d = new Dictionary<int, string>() { 
    {3, "C"},
    {2, "B"},
    {1, "A"},
};

var s = d.OrderBy(i => i.Value);

d = s;
like image 757
Boppity Bop Avatar asked Jun 19 '14 15:06

Boppity Bop


1 Answers

Implies I can actually cast list to dictionary

Well, it implies that the cast would be valid at compile-time. It doesn't mean it will work at execution time.

It's possible that this code could work:

IOrderedEnumerable<KeyValuePair<string, string>> pairs = GetPairs();
Dictionary<string, string> dictionary = (Dictionary<string, string>) pairs;

... but only if the value returned by GetPairs() were a class derived from Dictionary<,> which also implemented IOrderedEnumerable<KeyValuePair<string, string>>. It's very unlikely that that's actually the case in normal code. The compiler can't stop you from trying, but it won't end well. (In particular, if you do it with the code in your question and with standard LINQ to Objects, it will definitely fail at execution time.)

You should stick with ToDictionary... although you should also be aware that you'll lose the ordering, so there's no point in ordering it to start with.

To show this with the code in your question:

Dictionary<int, string> d = new Dictionary<int, string>() { 
    {3, "C"},
    {2, "B"},
    {1, "A"},
};

var s = d.OrderBy(i => i.Value);

d = (Dictionary<int, string>) s;

That compiles, but fails at execution time as predicted:

Unhandled Exception: System.InvalidCastException: Unable to cast object of type 'System.Linq.OrderedEnumerable`2[System.Collections.Generic.KeyValuePair`2[System.Int32,System.String],System.String]' to type 'System.Collections.Generic.Dictionary`2[System.Int32,System.String]'.
at Test.Main()


As a bit of background, you can always cast from any interface type to a non-sealed class ("target"), even if that type doesn't implement the interface, because it's possible for another class derived from "target" to implement the interface.

From section 6.2.4 of the C# 5 specification:

The explicit reference conversions are:

  • ...
  • From any class-type S to any interface-type T, provided S is not sealed and provided S does not implement T.
  • ...

(The case where S does implement T is covered by implicit reference conversions.)

If you try to implicitly convert a value and there's no implicit conversion available, but there's an explicit conversion available, the compiler will give you the warning in your question. That means you can fix the compiler-error with a cast, but you need to be aware of the possibility of it failing at execution time.

Here's an example:

using System;

class Test
{
    static void Main()
    {
        IFormattable x = GetObject();
    }

    static object GetObject()
    {
        return DateTime.Now.Second >= 30 ? new object() : 100;
    }
}

Error message:

Test.cs(7,26): error CS0266: Cannot implicitly convert type 'object' to 
   'System.IFormattable'. An explicit conversion exists (are you missing a cast?)

So we can add a cast:

IFormattable x = (IFormattable) GetObject();

At this point, the code will work about half the time - the other half, it'll throw an exception.

like image 131
Jon Skeet Avatar answered Oct 29 '22 06:10

Jon Skeet