如何获得IEnumerable<;词典条目>;出自IDictionary

本文关键字:IDictionary gt 出自 何获得 IEnumerable lt | 更新日期: 2023-09-27 18:27:46

我基本上想这样做:

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的工作方式和我的扩展完全一样。

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

如何获得IEnumerable<;词典条目>;出自IDictionary

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

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().Current应该是什么类型回来KeyValuePair还是DictionaryEntry?与相同ICollection.CopyTo.应复制到的类型的实例大堆

我们决定如下:

  1. IEnumerableICollection接口实现将使用KeyValuePair<K,V>作为项类型
  2. IDictionary特定成员(GetEnumerator返回IDictionaryEnumerator)将使用DictionaryEntry作为项目类型

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

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

请注意,在非泛型上下文中使用Dictionary有一个特别奇怪的地方——Hashtable(仍然)作为DictionaryEntry项进行迭代很好,而Dictionary上的"正常"迭代会为您提供KeyValuePair项。

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

您正试图将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)))) + "}";

我投票决定结束这个问题,因为这个答案已经足够好了。

这是我的解决方案:

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告诉你什么,在这里不起作用吗。