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.

+3
source share
3 answers

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 and ICollection interface implementation will use KeyValuePair<K,V> as the item type.
  • IDictionary specific members ( GetEnumerator return IDictionaryEnumerator ) will use the DictionaryEntry as the item type.

The reason is that we are in the process of making changes where IEnumerator<T> will extend IEnumerator . It would be very strange if, walking along the hierarchy from Dictionary<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.

+4
source

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)))) + "}"; 
+1
source

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.

+1
source

Source: https://habr.com/ru/post/913510/


All Articles