РЕДАКТИРОВАТЬ: После более тщательной проверки это не кажется хорошей идеей, поскольку в исходном хэш-наборе менее 60 элементов, описанный ниже метод работает медленнее, чем просто создание нового хэш-набора.
ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: кажется, что это работает, но используйте на свой страх и риск, если вы собираетесь сериализовать клонированные хэш-наборы, вы, вероятно, хотите скопировать SerializationInfo m_siInfo.
Я также столкнулся с этой проблемойи сделал удар по нему, ниже вы найдете метод расширения, который использует FieldInfo.GetValue и SetValue для копирования обязательных полей.Это быстрее, чем использование HashSet (IEnumerable), насколько это зависит от количества элементов в исходном хэш-наборе.Для 1000 элементов разница примерно в 7 раз. Для 100 000 элементов это примерно в 3 раза.
Существуют и другие способы, которые могут быть даже быстрее, но на данный момент это избавило меня от узкого места.Я пытался использовать expresstrees и emitting, но наткнулся на контрольно-пропускной пункт, если я заставлю их работать, я обновлю этот пост.
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.Serialization;
public static class HashSetExtensions
{
public static HashSet<T> Clone<T>(this HashSet<T> original)
{
var clone = (HashSet<T>)FormatterServices.GetUninitializedObject(typeof(HashSet<T>));
Copy(Fields<T>.comparer, original, clone);
if (original.Count == 0)
{
Fields<T>.freeList.SetValue(clone, -1);
}
else
{
Fields<T>.count.SetValue(clone, original.Count);
Clone(Fields<T>.buckets, original, clone);
Clone(Fields<T>.slots, original, clone);
Copy(Fields<T>.freeList, original, clone);
Copy(Fields<T>.lastIndex, original, clone);
Copy(Fields<T>.version, original, clone);
}
return clone;
}
static void Copy<T>(FieldInfo field, HashSet<T> source, HashSet<T> target)
{
field.SetValue(target, field.GetValue(source));
}
static void Clone<T>(FieldInfo field, HashSet<T> source, HashSet<T> target)
{
field.SetValue(target, ((Array)field.GetValue(source)).Clone());
}
static class Fields<T>
{
public static readonly FieldInfo freeList = GetFieldInfo("m_freeList");
public static readonly FieldInfo buckets = GetFieldInfo("m_buckets");
public static readonly FieldInfo slots = GetFieldInfo("m_slots");
public static readonly FieldInfo count = GetFieldInfo("m_count");
public static readonly FieldInfo lastIndex = GetFieldInfo("m_lastIndex");
public static readonly FieldInfo version = GetFieldInfo("m_version");
public static readonly FieldInfo comparer = GetFieldInfo("m_comparer");
static FieldInfo GetFieldInfo(string name)
{
return typeof(HashSet<T>).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic);
}
}
}