Хорошо, а как насчет решения с небольшим небезопасным кодом? Просто для удовольствия?
ВНИМАНИЕ! Это кодируется для ОС Windows и 32-разрядных, но нет причины, по которой этот метод не может быть изменен для работы на 64-разрядных или других ОС. Наконец, я проверил это на 3,5 рамки. Я думаю, что это будет работать на 2.0 и 3.0, но я не тестировал. Если Редмонд изменит количество, тип или порядок переменных экземпляра между ревизиями или исправлениями, это не будет работать.
Но это быстро !!!
Это взламывает KeyedCollection, лежащие в его основе List <> и Dictionary <> и копирует все внутренние данные и свойства. Это хак, потому что для этого вам нужен доступ к закрытым внутренним переменным. Я в основном сделал некоторые структуры для KeyedCollection, List и Dictionary, которые являются частными переменными этих классов в правильном порядке. Я просто указываю на эти структуры, где находятся классы и вуаля ... вы можете связываться с приватными переменными !! Я использовал отражатель RedGate, чтобы увидеть, что делает весь код, чтобы я мог понять, что копировать. Тогда нужно просто скопировать некоторые типы значений и использовать Array.Copy в нескольких местах.
Результат: CopyKeyedCollection <,>, CopyDict <> и CopyList <>. Вы получаете функцию, которая может быстро скопировать словарь <>, и функцию, которая может быстро скопировать список <> бесплатно!
Одна вещь, которую я заметил при разработке всего этого, это то, что KeyedCollection содержит список и словарь, указывающие на одни и те же данные! Сначала я думал, что это расточительно, но комментаторы отметили, что KeyedCollection специально для случая, когда вам нужен упорядоченный список и словарь одновременно.
В любом случае, я программист на ассемблере / c, который некоторое время был вынужден использовать vb, поэтому я не боюсь делать подобные хаки. Я новичок в C #, поэтому скажите мне, если я нарушил какие-либо правила или если вы думаете, что это круто.
Кстати, я исследовал сборку мусора, и с GC это должно работать нормально. Я думаю, что было бы разумно, если бы я добавил немного кода, чтобы исправить немного памяти для мс, которые мы тратим на копирование. Вы, ребята, скажите мне. Я добавлю несколько комментариев, если кто-нибудь попросит их.
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Collections.ObjectModel;
using System.Reflection;
namespace CopyCollection {
class CFoo {
public int Key;
public string Name;
}
class MyKeyedCollection : KeyedCollection<int, CFoo> {
public MyKeyedCollection() : base(null, 10) { }
protected override int GetKeyForItem(CFoo foo) {
return foo.Key;
}
}
class MyObject {
public MyKeyedCollection kc;
// Copy constructor
public MyObject(MyObject that) {
this.kc = new MyKeyedCollection();
if (that != null) {
CollectionTools.CopyKeyedCollection<int, CFoo>(that.kc, this.kc);
}
}
}
class Program {
static void Main(string[] args) {
MyObject mobj1 = new MyObject(null);
for (int i = 0; i < 7; ++i)
mobj1.kc.Add(new CFoo() { Key = i, Name = i.ToString() });
// Copy mobj1
MyObject mobj2 = new MyObject(mobj1);
// add a bunch more items to mobj2
for (int i = 8; i < 712324; ++i)
mobj2.kc.Add(new CFoo() { Key = i, Name = i.ToString() });
// copy mobj2
MyObject mobj3 = new MyObject(mobj2);
// put a breakpoint after here, and look at mobj's and see that it worked!
// you can delete stuff out of mobj1 or mobj2 and see the items still in mobj3,
}
}
public static class CollectionTools {
public unsafe static KeyedCollection<TKey, TValue> CopyKeyedCollection<TKey, TValue>(
KeyedCollection<TKey, TValue> src,
KeyedCollection<TKey, TValue> dst) {
object osrc = src;
// pointer to a structure that is a template for the instance variables
// of KeyedCollection<TKey, TValue>
TKeyedCollection* psrc = (TKeyedCollection*)(*((int*)&psrc + 1));
object odst = dst;
TKeyedCollection* pdst = (TKeyedCollection*)(*((int*)&pdst + 1));
object srcObj = null;
object dstObj = null;
int* i = (int*)&i; // helps me find the stack
i[2] = (int)psrc->_01_items;
dstObj = CopyList<TValue>(srcObj as List<TValue>);
pdst->_01_items = (uint)i[1];
// there is no dictionary if the # items < threshold
if (psrc->_04_dict != 0) {
i[2] = (int)psrc->_04_dict;
dstObj = CopyDict<TKey, TValue>(srcObj as Dictionary<TKey, TValue>);
pdst->_04_dict = (uint)i[1];
}
pdst->_03_comparer = psrc->_03_comparer;
pdst->_05_keyCount = psrc->_05_keyCount;
pdst->_06_threshold = psrc->_06_threshold;
return dst;
}
public unsafe static List<TValue> CopyList<TValue>(
List<TValue> src) {
object osrc = src;
// pointer to a structure that is a template for
// the instance variables of List<>
TList* psrc = (TList*)(*((int*)&psrc + 1));
object srcArray = null;
object dstArray = null;
int* i = (int*)&i; // helps me find things on stack
i[2] = (int)psrc->_01_items;
int capacity = (srcArray as Array).Length;
List<TValue> dst = new List<TValue>(capacity);
TList* pdst = (TList*)(*((int*)&pdst + 1));
i[1] = (int)pdst->_01_items;
Array.Copy(srcArray as Array, dstArray as Array, capacity);
pdst->_03_size = psrc->_03_size;
return dst;
}
public unsafe static Dictionary<TKey, TValue> CopyDict<TKey, TValue>(
Dictionary<TKey, TValue> src) {
object osrc = src;
// pointer to a structure that is a template for the instance
// variables of Dictionary<TKey, TValue>
TDictionary* psrc = (TDictionary*)(*((int*)&psrc + 1));
object srcArray = null;
object dstArray = null;
int* i = (int*)&i; // helps me find the stack
i[2] = (int)psrc->_01_buckets;
int capacity = (srcArray as Array).Length;
Dictionary<TKey, TValue> dst = new Dictionary<TKey, TValue>(capacity);
TDictionary* pdst = (TDictionary*)(*((int*)&pdst + 1));
i[1] = (int)pdst->_01_buckets;
Array.Copy(srcArray as Array, dstArray as Array, capacity);
i[2] = (int)psrc->_02_entries;
i[1] = (int)pdst->_02_entries;
Array.Copy(srcArray as Array, dstArray as Array, capacity);
pdst->_03_comparer = psrc->_03_comparer;
pdst->_04_m_siInfo = psrc->_04_m_siInfo;
pdst->_08_count = psrc->_08_count;
pdst->_10_freeList = psrc->_10_freeList;
pdst->_11_freeCount = psrc->_11_freeCount;
return dst;
}
// these are the structs that map to the private variables in the classes
// i use uint for classes, since they are just pointers
// statics and constants are not in the instance data.
// I used the memory dump of visual studio to get these mapped right.
// everything with a * I copy. I Used RedGate reflector to look through all
// the code to decide what needed to be copied.
struct TKeyedCollection {
public uint _00_MethodInfo; // pointer to cool type info
// Collection
public uint _01_items; // * IList<T>
public uint _02_syncRoot; // object
// KeyedCollection
public uint _03_comparer; // IEqualityComparer<TKey>
public uint _04_dict; // * Dictionary<TKey, TItem>
public int _05_keyCount; // *
public int _06_threshold; // *
// const int defaultThreshold = 0;
}
struct TList {
public uint _00_MethodInfo; //
public uint _01_items; // * T[]
public uint _02_syncRoot; // object
public int _03_size; // *
public int _04_version; //
}
struct TDictionary {
// Fields
public uint _00_MethodInfo; //
public uint _01_buckets; // * int[]
public uint _02_entries; // * Entry<TKey, TValue>[]
public uint _03_comparer; // IEqualityComparer<TKey>
public uint _04_m_siInfo; // SerializationInfo
public uint _05__syncRoot; // object
public uint _06_keys; // KeyCollection<TKey, TValue>
public uint _07_values; // ValueCollection<TKey, TValue>
public int _08_count; // *
public int _09_version;
public int _10_freeList; // *
public int _11_freeCount; // *
}
}
}