代码之家  ›  专栏  ›  技术社区  ›  Dirk Groeneveld

在运行时在.NET中确定字段的记录

  •  2
  • Dirk Groeneveld  · 技术社区  · 14 年前

    我想把数以百万计的记录存起来。这些记录的字段在编译时无法确定。这些字段可能有不同的类型、一些双精度、一些整数、一些字符串等。因为我必须存储这么多的字段,所以我希望这些记录的内存表示尽可能高效。

    在C++中,我将使每个记录都具有固定大小的缓冲区,它保存所有数据,并确定缓冲区中读取数据的位置。在C中,我不能这样做(我能吗?).

    怎么办?使用ilgenerator在运行时生成结构?管理C++?使用字节数组[]?

    6 回复  |  直到 14 年前
        1
  •  1
  •   TcKs    14 年前

    可以使用发射IL生成动态类型。关于这种技术的漂亮文章是关于codeproject的: http://www.codeproject.com/KB/cs/Creating_Dynamic_Types2.aspx

        2
  •  1
  •   Richard Anthony Hein    14 年前

    我不确定这些限制是否会使它不可用,但是您可以在C(不安全代码)中使用固定大小的缓冲区。见 the MSDN docs

        3
  •  1
  •   Jim Mischel    14 年前

    这听起来像是使用C/C++联盟的东西。也就是说(如果我记得我的C):

    union Thing
    {
      int iThing;
      uint uThing;
      char * stringThing;
      double doubleThing;
    };
    

    它占用的内存与其中定义的最大类型相同。所以在这里我想它是8字节(对于双字节)。

    如果您知道事物的类型,可以访问相应的字段:

    Thing myThing = GetThing();
    int i = myThing.iThing;  // if you know it's an int
    

    你如何知道它的类型取决于你自己。

    不管怎样,正如你现在可能知道的,在C中没有一个联合,但是你可以用 StructLayout 在System.Runtime.InteropServices中找到的属性:

    [StructLayout(LayoutKind.Explicit)]
    struct Thing
    {
        [FieldOffset(0)]
        int iThing;
        [FieldOffset(0)]
        uint uThing;
        [FieldOffset(0)]
        string stringThing;
        [FieldOffset(0)]
        double doubleThing;
    }
    

    您可以创建数组或 List 没问题。当然,这是一个值类型,所以您必须记住值类型的语义。另外请注意,尽管此结构的大小只有8个字节(或者存储的最大值类型的大小),但它包含对存储在堆中的字符串的引用。也就是说,一个字符串的开销是4字节(64位8字节)加上字符串本身的存储空间。

    顺便说一下,还有更有效的存储字符串的方法。存储它们的方式取决于您是否希望修改它们以及引用它们的速度,但是您可以轻松地节省.NET存储英语和大多数西欧语言字符串所需空间的近50%。

        4
  •  0
  •   ChaosPandion    14 年前

    使用 List<List<object>> 会让你的任务简单很多,除非我完全错过了什么。您可以根据需要执行的操作类型选择更好的集合类型。

        5
  •  0
  •   Reinderien    14 年前

    你能用仿制药吗?我认为这是相当有效的。如:

    class Record
    {
        UntypedSpecialField f;
    }
    
    ...
    
    abstract class UntypedSpecialField f { }
    class SpecialField<T> : UntypedSpecialField { }
    
    ...
    List<Record> database;
    

    这样做的好处是,如果存在与所讨论的字段相关的代码共性,那么它们可以进入非类型化专用字段,并且您可以得到一个结构良好的OO系统。

        6
  •  0
  •   KeithS    14 年前

    我同意chaospandion:list>可能是.net中使用的最简单但高效的构造。单个列表可以保存约20亿条记录(int.maxvalue)或2GB(32位.NET中单个对象的最大数据大小)。由于记录本身是对其他对象的引用,所以每个元素都是一个4(或8)字节的intptr,使外部列表的最大大小约为5亿个元素(它在引擎盖下使用了一个数组)。对于大多数记录来说,内部列表都是一个简单的步骤,除非其中一个字段是斑点状的。

    如果你告诉我们这些数据是如何进入系统的,一个更好的答案会更容易给出。是从档案里找来的吗?数据库?外部外围设备的数据流,如气象站?

    说真的,我只想让托管运行时完成它的工作。甚至.NET中的数组在索引零点之前都有一些开销,不同于非托管C/C++,其中数组只是块O内存,其中数组[0 ]=&数组。在64位体系结构中,要处理您所谈论的内存类型(在32位体系结构中,整个进程的内存使用量必须小于2GB,包括clr、程序集、调用堆栈、对象开销和实际数据),而在64位体系结构中,内存受RAM和页面文件空间的限制;能力系统对内存的寻址比当前硬件大几个数量级。即使增加了托管对象的开销,也应该有足够的空间。