【问题标题】:Why is HashSet<T> attributed with MayLeakOnAbort, but Dictionary<K,V> not?为什么 HashSet<T> 归因于 MayLeakOnAbort,但 Dictionary<K,V> 没有?
【发布时间】:2014-06-02 04:22:01
【问题描述】:

我在尝试为 SQL Server 编写 CLR 过程时注意到,由于带有 [HostProtectionAttribute(SecurityAction.LinkDemand, MayLeakOnAbort = true)] 属性,因此不允许使用 HashSet。 SQL Server CLR 过程不允许使用设置了MayLeakOnAbort 的对象。好的,所以在 CLR 过程中要避免一些类,甚至可能三思而后行在 CLR 过程之外使用。奇怪的是Dictionary&lt;K,V&gt;没有同样受到限制。现在基于我对 HashSet 和 Dictionary 是什么的理解,我希望 Dictionary 应该具有 Hashset 的所有复杂性和 then 一些。那么,为什么 Dictionary 没有类似的限制呢?我正在“三思而后行使用HashSet&lt;T&gt;”并认真考虑改用字典,即使我没有编写CLR过程并且只需要一个可以快速测试复杂键成员资格的集合(未定义比较、散列或相等接口的对象的对象引用)。我最好使用哈希集或字典吗? Hashset 的不同之处在于它允许使用没有比较或相等接口的类,纯粹基于内存地址或其他可能是 HashSet 不太“干净”的原因?

【问题讨论】:

    标签: c# sql-server memory hashset sqlclr


    【解决方案1】:

    字典是基于HashTable,而不是HashSet。虽然它们在概念上非常相似,但 HashSet 的实现包含一些不安全的方法,而 HashTable 和 Dictionary 则没有。

    Dictionary 主要使用 HashTable 作为加速键空间搜索的一种手段。给定用于字典键的类型的 GetHashCode() 的有效实现,字典中的查找是最佳情况下的常数时间和最坏情况下的线性时间。

    HashSet 是一个仅用于存储唯一值的集合(无键机制),并且需要在您的类上正确实现 GetHashCode 才能正常运行。

    哈希表和字典用于通过键查找值。 HashSet 仅用于维护一组唯一对象,没有键控机制。

    如果您不需要唯一性保证,或由实现 ISet 的东西提供的其他功能,则没有真正的理由使用 HashSet 代替数组或列表。

    如果您需要通过键将您的项目从集合中取出,请使用 HashTable 或字典(首选字典,因为它是通用感知的,因此您不会经常对所有内容进行装箱/拆箱)。

    查看这些链接以获得解释:

    http://msdn.microsoft.com/en-us/library/bb397727(v=vs.110).aspx

    http://msdn.microsoft.com/en-us/library/4yh14awz(v=vs.110).aspx

    【讨论】:

    • 这多半是错的,字典和集合类的区别其实很小。
    • 字典是键/值对的集合。 Set 和 HashSet 只是值的集合。对于大多数用例来说,它们根本不同。
    • 字典,在概念层面上,只是一个HashSet&lt;KeyValuePair&gt;,对公共 API 做了一些调整。底层实现将是相同的基本算法,即使这里或那里有一些差异。
    • 它们不是可互换的类型,不应该这样使用。 (是的,您可以制作一个包含键和所有空值的字典,但这是一个带有额外浪费内存的 HashSet)。如果您只需要一组没有键的唯一对象,请使用哈希集。如果您需要通过键检索特定对象,则使用字典。如果您需要一个索引的、可排序的对象集合,其中可能存在重复,请使用列表或数组。如果用例与正在使用的类不兼容,那么底层实现对于这个问题并不是非常重要。
    • 问题是关于实施细节,因此实施很重要。在这种情况下,两种实现都非常相似,这就提出了为什么只有一种实现可能泄漏的问题。
    【解决方案2】:

    HashSet&lt;T&gt; 包含诸如IntersectWith 之类的方法,这些方法使用stackalloc 使用不安全代码实现。 Dictionary&lt;TKey, TValue&gt; 不包含任何此类方法。虽然可以将自己的程序集标记为不安全,并避免使用有风险的方法,但我只是放弃并在 SQL CLR 函数中使用了Dictionary&lt;T, bool&gt;,其中所有值都是true,正是出于这个原因。

    【讨论】:

    • 但是,为什么它会泄漏内存?
    • @Servy 实际上,虽然它是不安全的代码,但它仍然是托管代码,我很惊讶stackalloc 会是一个问题。
    • @BlueMonkMN 至少(事后看来),IntersectWith 和其他人可以作为扩展方法实现,因此 HashSet&lt;T&gt; 本身是安全的,只有 HashSetExtensions 不会.
    • @BlueMonkMN 如果您要突破反射,那么您肯定不会获得积极的性能提升。反射增加了一些相当大的性能成本。即使坚持使用公共 API 也可能比使用反射更好。
    • @Servy 只要将当前私有的字段设置为内部字段,我假设的 HashSetExtensions 类就可以完成 HashSet 方法现在所做的事情,不是吗?
    猜你喜欢
    • 2010-11-06
    • 1970-01-01
    • 2012-08-04
    • 2015-09-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-07
    • 2010-09-21
    相关资源
    最近更新 更多