可变结构与类?

我不确定是使用可变结构还是可变类。 我的程序存储了一个包含大量对象的数组。 我注意到使用类会使所需的内存量翻倍。 但是,我希望对象是可变的,并且我被告知使用可变结构是邪恶的。 这就是我的类型:

struct /* or class */ Block { public byte ID; public bool HasMetaData; // not sure whether HasMetaData == false or // MetaData == null is faster, might remove this public BlockMetaData MetaData; // BlockMetaData is always a class reference } 

像这样分配大量的对象(注意下面的两个代码都运行了81次):

 // struct Block[,,] blocks = new Block[16, 256, 16]; 

使用大约35 MiB的内存,同时这样做:

 // class Block[,,] blocks = new Block[16, 256, 16]; for (int z = 0; z < 16; z++) for (int y = 0; y < 256; y++) for (int x = 0; x < 16; x++) blocks[x, y, z] = new Block(); 

使用约100 MiB的ram。

总而言之,我的问题如下:

我应该为块类型使用结构或类吗? 实例应该是可变的并存储一些值加上一个对象引用。

首先,如果你真的想节省内存,那么不要使用结构或类。

 byte[,,] blockTypes = new byte[16, 256, 16]; BlockMetaData[,,] blockMetadata = new BlockMetaData[16, 256, 16]; 

你想在内存中将类似的东西紧紧包装在一起 。 如果你可以避免它,你永远不想在结构中的引用旁边放一个字节; 这样的结构会自动浪费三到七个字节。 引用必须在.NET中进行字对齐。

其次,我假设你在这里建造一个体素系统。 根据它们的分布,可能有更好的方式来表示体素而不是三维arrays。 如果你要制作真正大量的这些东西,那么将它们存储在一个不可变的八叉树中 。 通过使用不可变八叉树的持久性属性,只要您所代表的宇宙是“块状”,您就可以制作具有四十个体素的立方结构。 也就是说,全世界都存在大的相似区域。 你可以用更大的O(lg n)时间来访问和更改元素,但是你可以使用更多的元素。

第三,“ID”是表示“类型”概念的一种非常糟糕的方式。 当我看到“ID”时,我假设该数字唯一地标识该元素,而不是描述它。 考虑将名称更改为不那么令人困惑的东西。

第四,有多少元素有元数据? 如果具有元数据的元素数量与元素总数相比较小,则可能比引用数组更好。 考虑一种稀疏数组方法; 稀疏数组的空间效率更高。

他们真的必须变得可变吗? 你总是可以使用一个不可变的结构来创建一个不同的字段的新值:

 struct Block { // I'd definitely get rid of the HasMetaData private readonly byte id; private readonly BlockMetaData metaData; public Block(byte id, BlockMetaData metaData) { this.id = id; this.metaData = metaData; } public byte Id { get { return id; } } public BlockMetaData MetaData { get { return metaData; } } public Block WithId(byte newId) { return new Block(newId, metaData); } public Block WithMetaData(BlockMetaData newMetaData) { return new Block(id, newMetaData); } } 

我还是不确定我是否会把它变成一个结构,说实话 – 但我怀疑,无论如何我都试图让它变成一个结构。

在内存和速度方面,您的性能要求是什么? 不可变类有多接近这些要求?

结构数组将提供比具有相同字段的不同类实例的不可变引用数组更好的存储效率,因为后者将需要前者所需的所有内存,除了内存以管理所需的类实例和内存举行参考。 所有这一切,你设计的结构有一个非常低效的布局。 如果你真的关心空间,并且每个项目实际上需要独立存储一个字节,一个布尔值和一个类引用,你最好的选择是要么有两个字节数组(一个字节实际上小于一个布尔值) )和一个类引用数组,或者有一个字节数组,一个像BitVector32这样的1/32元素的数组,以及一个类引用数组。