首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >复制KeyedCollection的最快方法

复制KeyedCollection的最快方法
EN

Stack Overflow用户
提问于 2009-06-11 22:40:57
回答 4查看 3.2K关注 0票数 2

我正在开发一个C#对象复制构造函数,其中一部分涉及到将KeyedCollection的内容复制到新的KeyedCollection中。这是我目前已经实现的:

代码语言:javascript
复制
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)都需要对现有数据进行唯一性检查,即使我知道它来自保证唯一性的来源。

怎样才能让这个复制构造函数尽可能快呢?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2009-06-13 00:49:49

好吧,用一些不安全的代码来解决这个问题怎么样?只是为了好玩?

警告!这是为windows OS和32位编写的,但没有理由不能将此技术修改为适用于64位或其他操作系统。最后,我在3.5框架上测试了它。我认为它可以在2.0和3.0上工作,但我没有测试。如果Redmond在修订或补丁之间更改实例变量的数量、类型或顺序,那么这将不起作用。

但这太快了!

它侵入KeyedCollection、它的底层List<>和Dictionary<>,并复制所有内部数据和属性。这是一个技巧,因为要做到这一点,你必须访问私有内部变量。我基本上为KeyedCollection、List和Dictionary做了一些结构,它们是这些类的私有变量,顺序是正确的。我只需将这些结构指向类所在的位置,voila...you就可以处理私有变量!!我使用RedGate反射器来查看所有代码都在做什么,这样我就可以知道要复制什么。然后,只需复制一些值类型,并在几个地方使用Array.Copy。

结果是CopyKeyedCollection<,>,CopyDict<>和CopyList<>。你会得到一个可以快速复制Dictionary<>的函数和一个可以免费快速复制List<>的函数!

在解决所有问题时,我注意到一件事,KeyedCollection包含一个列表和一个字典,它们都指向相同的数据!一开始我认为这很浪费,但是评论者指出KeyedCollection是专门为同时需要有序列表和字典的情况而设计的。

无论如何,我是一个汇编/c程序员,被迫使用vb一段时间,所以我不怕这样做。我是C#的新手,所以请告诉我我是否违反了任何规则,或者您是否认为这很酷。

顺便说一句,我研究了垃圾收集,这应该可以很好地与GC一起工作。我认为如果我添加一些代码来修复我们复制所花费的ms的一些内存,这将是谨慎的。你们来告诉我。如果有人请求em,我会添加一些注释。

代码语言:javascript
复制
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;                    // *
    }

  }


}
票数 3
EN

Stack Overflow用户

发布于 2009-06-11 22:56:08

我刚刚运行了一个测试,将10,000,000个条目和添加到不同的集合中,KeyedCollection花费的时间大约是列表的7倍,但仅比Dictionary对象长约50%。考虑到KeyedCollection是这两者的组合,Add的性能是完全合理的,并且它运行的重复键检查显然不会占用太多时间。您可能想要在您的KeyedCollection上运行类似的测试,如果速度明显变慢,您可以开始寻找其他地方(检查您的MyObject.Key获取方法,以确保您不会因此而获得开销)。

旧的回应

您是否尝试过:

代码语言:javascript
复制
this.kc = that.kc.MemberwiseClone() as MyKeyedCollection;

有关MemberwiseClone here的更多信息。

票数 3
EN

Stack Overflow用户

发布于 2009-06-11 22:55:30

您可以尝试序列化对象,然后将其反序列化为新对象-我不知道这是否会获得任何性能,但它可能会。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/984143

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档