How to get IEnumerable <DictionaryEntry> from IDictionary?
I really 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 listing. I can do
foreach(DictionaryEntry e in (IDictionary)obj)
So why can't I do this?
Iβm very 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 that I can replace the extension as follows:
return dict.Cast<DictionaryEntry>();
But that raises an exception. Is this a bug in Resharper? Or how does Cast
work? I would suggest that Cast
would work exactly as my extension does.
Edit: Ah .. I read this answer a little closer. Still weird.
Are keys and values ββknown at compile time? If so, you can make a general method for beautiful printing and let the compiler infer the types for you, for example:
static void Main(string[] args) { var obj = new Dictionary<string, string>() { { "foo", "bar" }, { "baz", "ack" }, }; var stringVersion = GetStringVersion(obj); Console.WriteLine(stringVersion); } private static string GetStringVersion<TKey, TValue>(Dictionary<TKey, TValue> dict) { return "{" + string.Join(Environment.NewLine, dict.Select(e => string.Format(" {0}: {1}", e.Key, e.Value))) + "}"; }
In terms of the behavior you see, this is a known strange case due to backward compatibility and design decision:
http://blogs.msdn.com/b/bclteam/archive/2004/09/03/225473.aspx
We discussed a problem with implementing IEnumerable on a Dictionary. What type should be IEnumerable.GetEnumerator (). return? KeyValuePair or DictionaryEntry? The same goes for ICollection.CopyTo. What type of instances should be copied to the array?
We have decided the following:
IEnumerable
andICollection
interface implementation will useKeyValuePair<K,V>
as the item type.IDictionary
specific members (GetEnumerator
returnIDictionaryEnumerator
) will use theDictionaryEntry
as the item type.The reason is that we are in the process of making changes where
IEnumerator<T>
will extendIEnumerator
. It would be very strange if, walking along the hierarchy fromDictionary<K,V>
βIEnumerable<T>
βIEnumerable
, we would unexpectedly change the type of the element returned from the counters.
Due to this design decision (when creating .NET 2.0), when you work with non-generic IEnumerable, calling GetEnumerator () returns an IDictionaryEnumerator, and the resulting elements are of type DictionaryEntry (presumably for back-compat with iteration of type Hashtable). However, as soon as you call a generic IEnumerable (this is what happens when you do Cast () or OfType (), you will get KeyValuePair elements instead.
Please note that this is a special oddity in using the Dictionary in an nonequivalent context. Hashtable (still) iterates as DictionaryEntry elements, and "normal" dictionary iteration gives you KeyValuePair elements.
As others have said, you should stick to shared access if you can, as this will allow you to avoid this small βgainβ in dictionary design decisions.
You are trying to include IDictionary itself in a DictionaryEntry. In the foreach loop, you do not put the IDictionary, but the elements contained in it.
You can do this and use a list:
var list = new List<DictionaryEntry>(); foreach (DictionaryEntry de in (IDictionary)obj) list.Add(de); return "{" + string.Join(Environment.NewLine, list.Select(e => string.Format(" {0}: {1}", PrettyString(e.Key), PrettyString(e.Value)))) + "}";
I voted to close this question as this answer is good enough.
This is my decision:
static class EnumerableExt { public static IEnumerable<DictionaryEntry> Entries(this IDictionary dict) { foreach (var item in dict) yield return (DictionaryEntry)item; } }
note that
return dict.Cast<DictionaryEntry>();
Doesn't work here, despite what Resharper tells you.