将一个KeyValuePair类型的LINQ结果列表强制转换为字典 - 缺少概念

6

我想写这个LINQ语句:

Dictionary<int, ItemBO> result = ( Dictionary<int, ItemBO> )
    ( from item in originalResults where item.Value.SomeCriteria == true
    select item );

originalResults 是一个 Dictionary<int, ItemBO> 类型。

我知道 item 是一个 KeyValuePair<int, ItemBO> 类型,但我本来以为从这个类型的列表转换成同类型的字典应该是很自然的事情。

然而,为了让编译器闭嘴,我不得不写出以下代码:

Dictionary<int, ItemBO> result = 
    ( from item in originalResults where item.Value.SomeCriteria == true
    select item.Value ).ToDictionary( GetItemKey );

尽管这并不完全违背直觉,但这表明在内部进行了大量不必要的字典解包和重打包工作。是否有更好的解决方案?我是否缺少某个概念?

4个回答

4
尽管这并不完全违反直觉,但这表明在内部进行了许多不必要的解包和重新打包字典的工作。
我不确定你所说的“不必要”的工作是什么意思。执行此操作需要大量工作-您的查询必须完全评估,这涉及扫描整个字典并为每个条目评估谓词。此外,必须创建并填充新字典以存储结果。我认为,如果这项工作被自动转换隐藏起来,那将更成问题。
真正的问题是,您正在反向使用字典。按键查找很快,但按值查找需要扫描字典中的所有项目。如果您可以将字典存储为筛选器使用的值,则可以进行更快速的查找,并具有更清晰的语法。

我原本并没有认为我在反向使用我的字典,但现在你让我重新考虑这个模块的整体架构。(我继承了它。它确实需要重写,而这可能是滴定点。) - Bob Kaufman
@Bob Kaufman:你的代码中可能还有其他地方在“正确”的方式下使用了字典,而这个地方是个例外。如果是这样的话,如果这个操作不经常被调用,那么保持现状也许是可以接受的。但如果这是你通常使用字典的方式,我建议考虑是否有更适合的设计方案。 - Mark Byers

2

我会再试着解释一下你错过的真正“关键”概念。

如果我要猜的话,我想你可能认为Dictionary<TKey, TValue>就是它所包含内容的同义词。你有一堆键值对,那就是一个字典,对吧?但这里你漏掉了一个重要细节。虽然你当然可以使用一个简单的KeyValuePair<TKey, TValue>[]实现IDictionary<TKey, TValue>,但这样做会失去拥有字典类型的最大好处之一:通过键快速查找。

即使你保持数组排序,并使用二分搜索定位键(顺便说一句,这会导致所有插入都产生成本),也无法与Dictionary<TKey, TValue>使用的哈希表实现竞争。这种实现不仅仅是“一堆键值对”,没有特定的结构;对于这种类型来说,结构就是一切。因此,从一堆键值对(没有结构的内容)快速转换到字典(具有非常特定结构的类型)并不是真正可能的——尽管在C#中,一个类型的结构布局并不真正决定是否可以进行强制转换(你必须仅遵守继承,以及一些内置和可能的用户定义类型转换)。

这样想吧:字典是容器,而不是内容,对吧?所以如果我有一个装着许多不同类型饼干的罐子,我想取出所有巧克力曲奇饼干,我可能会把每个饼干提取出来,放在一堆小饼干上然后说:“现在这是一个巧克力曲奇饼干罐,对吧?”不是这样的,我只是取出了饼干;我得到的是一堆饼干,而不是罐子。如果我想要一个装有巧克力曲奇饼干的罐子,那我就必须把它们放进去——这将需要一些非零的工作。


1
你所缺失的概念是,你的代码代表了一个现有字典的查询,查询结果将是一个 IEnumerable<KeyValuePair<K,V>>。你原来的代码期望的是,有人为 IEnumerable<KeyValuePair<K,V>> 定义了一个转换成 Dictionary<K,V> 的方法。实质上,他们给了你定义任何所需转换的能力,因为你有 ToDictionary 扩展方法可以让你确定一个键、一个值等。

0

linq查询的“真正”结果不是字典,因此您无法自然地将其转换。 结果是IQueryable,因此您必须显式转换它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接