I essentially want to do this:
if(obj is IDictionary)
{
return "{" + string.Join(Environment.NewLine, ((IDictionary)obj).Cast<DictionaryEntry>().Select(e => string.Format(" {0}: {1}", PrettyString(e.Key), PrettyString(e.Value)))) + "}";
}
But I keep getting an invalid cast. I can do
foreach(DictionaryEntry e in (IDictionary)obj)
So why can’t I do this?
I find this quite curious. I wrote an extension to solve the problem:
static class EnumerableExt
{
public static IEnumerable<DictionaryEntry> Entries(this IDictionary dict)
{
foreach (var item in dict) yield return (DictionaryEntry)item;
}
}
Then I can just do:
((IDictionary)obj).Entries().Select(...)
The curious part is that Resharper tells me I can replace my extension with this:
return dict.Cast<DictionaryEntry>();
But that throws an exception. Is this a bug in Resharper? Or how Cast works? I’d imagine Cast would work exactly the way my extension does.
Edit: Ah.. I read this answer a bit more closely. Still strange.
Are the key and value types known at compile time? If so, you could make a generic method for the pretty-print and let the compiler infer the types for you, like:
In terms of the behavior you’re seeing, it’s a known weird case due to backwards compat and a design decision:
http://blogs.msdn.com/b/bclteam/archive/2004/09/03/225473.aspx
Due to that design decision (during the creation of .NET 2.0), when you operate on the non-generic IEnumerable, the GetEnumerator() call returns IDictionaryEnumerator, and the resulting items are of type DictionaryEntry (presumably for back-compat with Hashtable-type iteration). However, once you are calling the generic IEnumerable (which is what happens once you do Cast() or OfType(), you’ll get KeyValuePair items instead.
Note that this is a particular oddity about using Dictionary in a non-generic context – Hashtable (still) iterates as DictionaryEntry items just fine, and ‘normal’ iteration over Dictionary gives you KeyValuePair items.
As others have said, you should stick with the generic access if you can, since it’ll let you avoid this little ‘gotcha’ in the design decisions of Dictionary.