如何从IDictionary中获取IEnumerable ?(How to get an IEnumerable out of an IDictionary?)

编程入门 行业动态 更新时间:2024-10-20 20:48:49
如何从IDictionary中获取IEnumerable ?(How to get an IEnumerable out of an IDictionary?)

我基本上想要这样做:

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

但我一直得到一个无效的演员。 我可以

foreach(DictionaryEntry e in (IDictionary)obj)

那我为什么不能这样做呢?


我觉得这很好奇。 我写了一个扩展来解决问题:

static class EnumerableExt { public static IEnumerable<DictionaryEntry> Entries(this IDictionary dict) { foreach (var item in dict) yield return (DictionaryEntry)item; } }

然后我可以这样做:

((IDictionary)obj).Entries().Select(...)

好奇的部分是Resharper告诉我我可以用这个替换我的扩展:

return dict.Cast<DictionaryEntry>();

但这引发了一个例外。 这是Resharper中的一个错误吗? 或者Cast如何运作? 我想Cast会完全按照我的扩展方式工作。

编辑:啊..我更仔细地阅读了这个答案 。 还是很奇怪。

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.

最满意答案

编译时是否知道键和值类型? 如果是这样,你可以为漂亮的打印制作一个通用的方法,让编译器为你推断出类型,比如:

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

就你所看到的行为而言,由于反向竞争和设计决定,这是一个众所周知的奇怪案例:

http://blogs.msdn.com/b/bclteam/archive/2004/09/03/225473.aspx

我们讨论了在Dictionary上实现IEnumerable的问题。 IEnumerable.GetEnumerator()应该是什么类型。当前返回? KeyValuePair还是DictionaryEntry? ICollection.CopyTo也是如此。 应该将哪种类型的实例复制到数组?

我们决定如下:

IEnumerable和ICollection接口实现将使用KeyValuePair<K,V>作为项类型。 IDictionary特定成员( GetEnumerator返回IDictionaryEnumerator )将使用DictionaryEntry作为项类型。

原因是我们正在进行一个改变, IEnumerator<T>将扩展IEnumerator 。 如果从Dictionary<K,V> - > IEnumerable<T> - > IEnumerable走出层次结构,我们突然改变了从枚举器返回的项的类型,这将是非常奇怪的。

由于该设计决策(在创建.NET 2.0期间),当您对非通用IEnumerable进行操作时,GetEnumerator()调用返回IDictionaryEnumerator,并且结果项的类型为DictionaryEntry(可能用于带有Hashtable的反向compat-类型迭代)。 但是,一旦调用了通用的IEnumerable(一旦你执行Cast()或OfType()就会发生这种情况),你将获得KeyValuePair项目。

请注意,在非泛型上下文中使用Dictionary是一个特别奇怪的事情--Hashtable(仍然)迭代为DictionaryEntry项目就好了,而且在Dictionary上的'normal'迭代为你提供了KeyValuePair项。

正如其他人所说,如果可以的话,你应该坚持使用通用访问,因为它可以让你在Dictionary的设计决策中避免这个小小的“陷阱”。

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:

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'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

We discussed a problem with implementation of IEnumerable on Dictionary. What type should IEnumerable.GetEnumerator().Current return? KeyValuePair or DictionaryEntry? Same for ICollection.CopyTo. Instances of what type should be copied to the array?

We decided the following:

IEnumerable and ICollection interface implementations will use KeyValuePair<K,V> as the item type. IDictionary specific members (GetEnumerator returning IDictionaryEnumerator) will use DictionaryEntry as the item type.

The reason is that we are in a process of making a change where IEnumerator<T> would extend IEnumerator. It would be very strange if walking the hierarchy from Dictionary<K,V>->IEnumerable<T>->IEnumerable we suddenly changed the type of the item returned from enumerators.

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.

更多推荐

本文发布于:2023-08-07 08:14:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1463769.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:IDictionary   IEnumerable

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!