如何从IDictionary中获取IEnumerable <dictionaryentry>?</dictionaryentry>

时间:2012-03-15 02:45:13

标签: c#

我基本上想要这样做:

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将完全按照扩展程序的方式工作。

编辑:啊..我更仔细地阅读了this answer。还是很奇怪。

3 个答案:

答案 0 :(得分:4)

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

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

  

我们讨论了IEnumerable实现的问题   字典。 IEnumerable.GetEnumerator()。当前应该是什么类型   返回? KeyValuePair还是DictionaryEntry?同样的   ICollection.CopyTo。应该将哪种类型的实例复制到   阵列?

     

我们决定了以下内容:

     
      
  1. IEnumerableICollection接口实施将使用KeyValuePair<K,V>作为项目类型。
  2.   
  3. IDictionary具体成员(GetEnumerator返回IDictionaryEnumerator)将使用DictionaryEntry作为项目类型。 < / LI>         

    原因是我们正在IEnumerator<T>扩展IEnumerator的过程中进行更改。从Dictionary<K,V> - &gt; IEnumerable<T> - &gt; IEnumerable走过层次结构会非常奇怪,我们突然改变了枚举器返回的项目类型。

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

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

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

答案 1 :(得分:1)

您正在尝试将IDictionary本身转换为DictionaryEntry。在foreach循环中,您不会转换IDictionary,而是包含其中的项目。

您可以执行此操作并使用列表:

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

答案 2 :(得分:1)

我投票结束了这个问题,因为this answer已经足够了。

这是我的解决方案:

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

请注意

 return dict.Cast<DictionaryEntry>();

在这里工作,尽管Resharper告诉你。