克隆HashSet <T>的有效方法?

Tho*_*que 35 .net performance clone hashset

几天前,我回答了一个关于SO 的有趣问题HashSet<T>.一个可能的解决方案涉及克隆hashset,在我的回答中我建议做这样的事情:

HashSet<int> original = ...
HashSet<int> clone = new HashSet<int>(original);
Run Code Online (Sandbox Code Playgroud)

虽然这种方法非常简单,但我怀疑它的效率非常低:新构造函数HashSet<T>需要单独添加原始hashset中的每个项目,并检查它是否已经存在.这显然是浪费时间:因为源集合是a ISet<T>,所以保证不包含重复项.应该有办法利用这些知识......

理想情况下,HashSet<T>应该实施ICloneable,但遗憾的是并非如此.我还检查了Reflector,看看如果HashSet<T>源集合是一个哈希集,构造函数是否做了特定的事情,但事实并非如此.它可能可以通过在私有字段上使用反射来完成,但这将是一个丑陋的黑客......

那么,有人提出了一个更有效地克隆哈希集的聪明解决方案吗?

(请注意,这个问题纯粹是理论上的,我不需要在真实的程序中这样做)

jth*_*thg 10

如果你真的想要最有效的克隆方法HashSet<T>,你可以做以下事情(但可能以可维护性为代价)

  1. 使用反射器或调试器确切地确定HashSet<T>需要复制的字段.您可能需要为每个字段递归执行此操作.
  2. 使用Reflection.Emit或使用表达式树来生成一个方法,该方法可以对所有字段进行必要的复制.可能需要调用其他生成的方法来复制每个字段的值.我们正在使用运行时代码生成,因为它是直接访问私有字段的唯一方法.
  3. 使用FormatterServices.GetUninitializedObject(...)实例化一个空对象.使用步骤2中生成的方法将原始对象复制到新的空白对象.


Maf*_*osh 7

我检查了版本4.5.2和版本4.7.2的 .NET Framework 源代码。版本 4.7.2 确实在构造函数中进行了优化,以在传入的集合类型为 HashSet 时使用一些内部克隆逻辑进行处理。您还需要将比较器传递到构造函数中才能使此逻辑正常工作。版本 4.5.2 似乎没有这种优化。

例子:

var clonedSet = new HashSet(set, set.Comparer);
Run Code Online (Sandbox Code Playgroud)

  • 是的,我想它是在 4.6 中添加的。 (2认同)