C# 复制KeyedCollection的最快方法
我正在研究一个C#对象复制构造函数,其中一部分涉及将KeyedCollection的内容复制到一个新的KeyedCollection中。这是我目前实施的:C# 复制KeyedCollection的最快方法,c#,.net,optimization,collections,keyedcollection,C#,.net,Optimization,Collections,Keyedcollection,我正在研究一个C#对象复制构造函数,其中一部分涉及将KeyedCollection的内容复制到一个新的KeyedCollection中。这是我目前实施的: class MyKeyedCollection : KeyedCollection<uint, DataObject> { protected override uint GetKeyForItem( DataObject do ) { return do.Key; } } class M
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 );
}
}
}
类MyKeyedCollection:KeyedCollection
{
受保护的覆盖uint GetKeyForItem(DataObject do)
{
返回do.Key;
}
}
类MyObject
{
私人MyKeyedCollection kc;
//复制构造函数
公共MyObject(MyObject that)
{
this.kc=新的MyKeyedCollection();
foreach(DataObject do在that.kc中)
{
本.kc.Add(do);
}
}
}
这样做是正确的——集合按预期进行复制。问题是它也有点慢。我猜问题在于每个.Add(do)都需要对现有数据进行唯一性检查,尽管我知道它来自一个保证唯一性的源
如何使此复制构造函数尽可能快?您可以尝试序列化对象,然后将其反序列化为一个新对象-我不知道这是否会获得任何性能,但它可能会获得任何性能。我刚刚运行了一个测试,将10000000个项添加到各种集合中,KeyedCollection花费了大约7倍于列表的时间,但只比Dictionary对象长约50%。考虑到KeyedCollection是这两者的组合,Add的性能是完全合理的,并且它运行的重复密钥检查显然不会占用那么多时间。您可能希望在KeyedCollection上运行类似的测试,如果速度明显变慢,您可以开始查找其他地方(检查
MyObject.Key
getter以确保不会因此而增加开销)
旧反应 您是否尝试过:
this.kc = that.kc.MemberwiseClone() as MyKeyedCollection;
更多关于MemberwiseClone的信息。如果您经常这样做,建议您改为使用不可变集合 这些结构不是您直接修改的,而是“修改”返回一个新对象,该对象可能使用旧对象状态,但反映您所做的更改 Net提供了各种不可变的字典/集合/基于树的映射(许多是f#中的,因为它更适合这种开发风格)
Eric Lippert有一些非常好的解决方案,应该与您想要的非常接近。好的,用一些不安全的代码来解决如何?只是为了好玩 警告!这是为windows操作系统和32位操作系统编写的,但没有理由不修改这项技术以适用于64位或其他操作系统。最后,我在3.5框架上对此进行了测试。我想它会在2.0和3.0上运行,但我没有测试。如果Redmond在修订版或补丁之间更改实例变量的数量、类型或顺序,那么这将不起作用 但是这很快 这会侵入KeyedCollection、其底层列表和字典,并复制所有内部数据和属性。这是一种黑客行为,因为要做到这一点,您必须访问私有内部变量。我基本上为KeyedCollection、List和Dictionary创建了一些结构,它们是这些类的私有变量,顺序正确。我只是简单地将这些结构指向类所在的位置,瞧……你可以搞乱私有变量!!我使用RedGate反射器查看所有代码都在做什么,这样我就可以知道要复制什么。然后只需复制一些值类型并使用Array.Copy在几个地方进行复制 结果是CopyKeyedCollection、CopyDict和CopyList。你可以得到一个功能,可以快速复制字典和一个可以快速复制免费列表 在完成这一切时,我注意到一件事,KeyedCollection包含一个列表和一个字典,它们都指向相同的数据!起初我认为这是浪费,但评论家指出KeyedCollection专门用于同时需要有序列表和字典的情况 无论如何,我是一名汇编/c程序员,被迫使用vb一段时间,所以我不怕做这样的黑客。我是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; // *
}
}
}
使用系统;
使用System.Collections.Generic;
使用系统文本;
使用System.Runtime.InteropServices;
使用System.Collections.ObjectModel;
运用系统反思;
命名空间CopyCollection{
类CFoo{
公钥;
公共字符串名称;
}
类MyKeyedCollection:KeyedCollection{
public MyKeyedCollection():base(null,10){}
受保护的覆盖int GetKeyForItem(CFoo foo){
返回foo.Key;
}
}
类MyObject{
公共MyKeyedCollection kc;
//复制构造函数
公共MyObject(MyObject that){
this.kc=新的MyKeyedCollection();
如果(该!=null){
CollectionTools.CopyKeyedCollection(that.kc,this.kc);
}
}
}
班级计划{
静态void Main(字符串[]参数){
MyObject mobj1=新的MyObject(空);
对于(int i=0;i<7;++i)
mobj1.kc.Add(new CFoo(){Key=i,Name=i.ToString()});
//复制mobj1
MyObject mobj2=新的MyObject(mobj1);
//向mobj2添加更多项目
对于(int i=8;i<712324;++i)
mobj2.kc.Add(new CFoo(){Key=i,Name=i.ToString()});
//复制mobj2
MyObject mobj3=新的MyObject(mobj2);
//在这里后面放一个断点,看看mobj,看看它是否有效!
//您可以从mobj1或mobj2中删除内容,并查看仍在mobj3中的项目,
}
}
公共静态类收集工具{
公共不安全静态KeyedCollection CopyKeyedCollection(
钥匙
///
/// 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;
}
[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
}