如何获得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
的工作方式和我的扩展完全一样。
编辑:啊。。我仔细阅读了这个答案。还是很奇怪。
键和值类型在编译时已知吗?如果是这样的话,你可以为漂亮的打印制作一个通用方法,让编译器为你推断类型,比如:
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.应复制到的类型的实例大堆
我们决定如下:
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类型迭代的反向兼容)。但是,一旦您调用了通用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告诉你什么,在这里不起作用吗。