[英]Fastest way to copy a KeyedCollection
我正在研究C#對象復制構造函數,其中一部分涉及將KeyedCollection的內容復制到新的KeyedCollection中。 這是我目前實現的:
class MyKeyedCollection : KeyedCollection<uint, DataObject>
{
protected override uint GetKeyForItem( DataObject do )
{
return do.Key;
}
}
class MyObject
{
private MyKeyedCollection kc;
// Copy constructor
public MyObject( MyObject that )
{
this.kc = new MyKeyedCollection();
foreach ( DataObject do in that.kc )
{
this.kc.Add( do );
}
}
}
這樣做是正確的-集合將按預期復制。 問題是它也有點慢。 我猜測問題是,每個.Add(do)都需要對現有數據進行唯一性檢查,即使我知道它來自保證唯一性的來源。
如何使此副本構造函數盡可能快?
我剛剛進行了一項測試,將10,000,000個項目添加到各種集合中,KeyedCollection的耗時是列表的7倍,但僅比Dictionary對象長50%。 考慮到KeyedCollection是這兩者的組合,添加的表現是完全合理的,而重復鍵檢查它運行顯然沒有考慮那么多時間。 您可能想在KeyedCollection上運行類似的測試,並且如果運行速度明顯變慢,則可以開始在其他地方查找(檢查MyObject.Key
getter以確保不會從中獲得開銷)。
你有沒有嘗試過:
this.kc = that.kc.MemberwiseClone() as MyKeyedCollection;
有關MemberwiseClone的更多信息,請參見此處 。
好的,帶有不安全代碼的解決方案怎么樣? 純娛樂?
警告! 這是針對Windows OS和32位編碼的,但是沒有理由不能將此技術修改為在64位或其他OS上運行。 最后,我在3.5框架上對此進行了測試。 我認為它可以在2.0和3.0上運行,但我沒有測試。 如果Redmond在修訂版或修補程序之間更改了實例變量的數量,類型或順序,則此方法將無效。
但這很快!!!
這會侵入KeyedCollection,其基礎的List <>和Dictionary <>,並復制所有內部數據和屬性。 這是一個hack,因為要執行此操作,您必須訪問私有內部變量。 我基本上為KeyedCollection,List和Dictionary構造了一些結構,這些結構是按正確順序排列的這些類的私有變量。 我只是將這些結構指向類所在的地方,瞧……您可能會弄亂私有變量! 我使用RedGate反射器查看所有代碼在做什么,因此我可以找出要復制的內容。 然后,只需復制一些值類型並在幾個地方使用Array.Copy即可。
結果是CopyKeyedCollection <,>, CopyDict <>和CopyList <>。 您將獲得一種可以免費快速復制Dictionary <>和免費快速復制List <>的功能!
在解決所有問題時,我注意到的一件事是KeyedCollection包含一個列表和一個字典,都指向相同的數據! 我原本以為這很浪費,但是評論家指出KeyedCollection明確適用於同時需要有序列表和字典的情況。
無論如何,我是一個不得不在一段時間內使用vb的程序集/ c程序員,所以我不怕這樣做。 我是C#的新手,所以請告訴我我是否違反了任何規則,或者您認為這很酷。
順便說一下,我研究了垃圾收集,這對於GC來說應該可以正常工作。 我認為如果我添加一些代碼來為我們花費的ms修復一些內存是明智的。 你們告訴我。 如果有人要求他們,我會添加一些評論。
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; // *
}
}
}
///
/// Clones Any Object.
/// </summary>
/// <param name="objectToClone">The object to clone.</param>
/// <return>The Clone</returns>
public static T Clone<T>(T objectToClone)
{
T cloned_obj = default(T);
if ((!Object.ReferenceEquals(objectToClone, null)) && (typeof(T).IsSerializable))
{
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bin_formatter = null;
Byte[] obj_bytes = null;
using (MemoryStream memory_stream = new MemoryStream(1000))
{
bin_formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
try
{
bin_formatter.Serialize(memory_stream, objectToClone);
}
catch (SerializationException) { }
obj_bytes = memory_stream.ToArray();
}
using (MemoryStream memory_stream = new MemoryStream(obj_bytes))
{
try
{
cloned_obj = (T)bin_formatter.Deserialize(memory_stream);
}
catch (SerializationException) { }
}
}
return cloned_obj;
}
注意:objectToClone必須是可序列化的,否則您將遇到異常並返回null。
您還需要創建自己的IDataObject,因為DataObject不可序列化:
[Serializable]
public class MyDataObject : IDataObject
{
public int mData;
public MyDataObject(int data)
{
mData = data;
}
#region IDataObject Members
public object GetData(Type format)
{
return mData;
}
#endregion
}
您可以嘗試序列化對象,然后將其反序列化為新對象-我無法確定這樣做是否會獲得任何性能,但可能會獲得。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.