关于c#:Structs vs classes

Structs versus classes

我将在代码中创建100000个对象。它们是小的,只有2或3个属性。我将把它们放在一个通用列表中,当它们存在时,我将循环它们并检查值a,并可能更新值b

将这些对象创建为类还是结构更快/更好?

编辑

a.属性是值类型(除了我认为的字符串?)

B.他们可能(我们还不确定)有一个验证方法

编辑2

我想知道:堆和堆栈上的对象是由垃圾收集器平均处理的,还是工作方式不同?


Is it faster to create these objects as class or as struct?

你是唯一能决定那个问题答案的人。尝试两种方法,测量一个有意义的、以用户为中心的、相关的性能指标,然后您将知道在相关场景中,更改是否对实际用户有意义的影响。

结构消耗较少的堆内存(因为它们更小并且更容易压缩,而不是因为它们"在堆栈上")。但它们的复制时间比参考复制要长。我不知道您的性能指标对于内存使用或速度是什么;这里有一个折衷方案,您知道它是什么。

Is it better to create these objects as class or as struct?

也许是类,也许是结构。根据经验:如果对象是:1。小的2。逻辑上不可变的值三。他们有很多然后我会考虑把它变成一个结构。否则我会坚持使用引用类型。

如果需要改变某个结构的某个字段,通常最好构建一个构造函数,该构造函数返回一个字段设置正确的新结构。这可能稍微慢一点(测量一下!)但逻辑上讲起来容易多了。

Are objects on the heap and the stack processed equally by the garbage collector?

不,它们不同,因为堆栈上的对象是集合的根。垃圾收集器不需要问"堆栈上的这个东西还活着吗?"因为这个问题的答案总是"是的,它在堆栈上"。(现在,您不能依赖它来保持对象的活动性,因为堆栈是一个实现细节。允许抖动引入优化,例如注册通常是堆栈值的值,然后它从不在堆栈上,因此GC不知道它仍然存在。一旦保存在其上的寄存器不再被读取,则已注册对象的子代就可以被积极地收集。)

但是垃圾收集器必须将堆栈上的对象视为活动对象,就像它对待任何已知为活动对象一样。堆栈上的对象可以引用需要保持活动状态的堆分配对象,因此GC必须将堆栈对象视为活动的堆分配对象,以确定活动集。但显然,为了压缩堆,它们不会被视为"活动对象",因为它们首先不在堆中。

明白了吗?


有时使用struct时,您不需要调用new()构造函数,直接分配字段,使其比通常更快。

例子:

1
2
3
4
5
6
Value[] list = new Value[N];
for (int i = 0; i < N; i++)
{
    list[i].id = i;
    list[i].is_valid = true;
}

比这快2到3倍

1
2
3
4
5
Value[] list = new Value[N];
for (int i = 0; i < N; i++)
{
    list[i] = new Value(i, true);
}

其中,Value是一个struct,有两个字段(id和is_valid)。

另一方面是需要移动的项或选定的值类型,所有这些复制都会减慢您的速度。为了得到准确的答案,我怀疑你必须分析你的代码并测试它。


结构可能看上去与阶级相似,但有重要的区别,你应该知道。首先,类别是参考类型,结构是价值型。通过结构,你可以创造出像建筑物类型一样的物体,并享受它们的利益。

当你在一个班级上呼叫新的操作员时,它会被分配到一个等级。然而,当你建立一个结构时,它会在堆栈上创造出来。这将带来收益。同时,你也不会被引用到一个机构的一个实例中,就像你是一个阶级。你将直接与组织机构合作。由于这一点,当经过一个结构到一种方法时,它通过一个参照值而转移。

这里更多

http://msdn.microsoft.com/en-us/library/a288471(vs.71)。


Arrays of structs are represented on the heap in a continuous block of memory,whereas an array of objects is represented as a continuous block of references with the actual objects themselves elsewhere on the heap,thus requiring memory for both the objects and for their array referen

在这个例子中,当你把它们放在一个List<>(和一个EDOCX1〕〔0〕的阵列中时,它会更有效,更便于使用。

(尽管如此,大型阵列将找到它们在大型物体的热点,如果它们的生命很长,可能对你的过程的记忆管理产生不利影响。)还记得,记忆不是唯一的考虑。)


如果它们有价值的话,那么你可能会使用一个结构。如果他们有参考语种,那么你可能需要使用一个分类。有一些例外,即使有价值的语种,也能从那里开始创造一个阶级。

就你的第二版而言,GC只处理热量,但空间比堆积空间更热量,所以把东西放到堆积空间并不总是一个温泉。最好的是,一份结构类型的清单和一份分类类型的清单将以第二种方式出现,因此这在本案中是不可接受的。

编辑:

我开始考虑到这一点。毕竟,如果不需要活跃的话,做一个变异的类别是一个坏主意,而我也不会用一个变异的结构来管理它。这是一个穷人的想法,常常是一个坏的想法,但最重要的是,它并不与价值观相吻合,因此,它并不意味着在第二个案例中使用一个结构。

在私营企业结构中,可以有合理的例外情况,在这种结构中,所有的使用都被限制在极为有限的范围内。这不是在这里。

事实上,我认为"这是一个坏的学习方法,它并不是一个更好的方法,它涉及的是头部和堆栈(在最起码的情况下,它具有某种影响,即使一个经常被误解的人)。"它变异了,所以它不应该把它看作是有价值的语义,所以它是一个坏的结构"只是略有不同,但重要的是我认为。


结构的本质就是字段的集合。在.NET中,结构可以"假装"为对象,并且对于每种结构类型.NET都隐式定义了具有相同字段和方法的堆对象类型,这些字段和方法(作为堆对象)的行为将类似于对象。保存对此类堆对象的引用的变量("boxed"结构)将显示引用语义,但直接保存结构的变量只是变量的聚合。

我认为结构与类的混淆很大程度上源于这样一个事实:结构有两个非常不同的用例,它们应该有非常不同的设计准则,但是MS准则没有区分它们。有时需要一些类似于对象的行为;在这种情况下,MS准则是相当合理的,尽管"16字节限制"可能更像24-32。然而,有时需要的是变量的聚合。用于此目的的结构应该简单地由一组公共字段组成,并且可能包含一个Equals重写、ToString重写和IEquatable(itsType).Equals实现。用作字段聚合的结构不是对象,不应假装是。从结构的角度来看,字段的含义不应大于或小于"写入此字段的最后一件事"。任何附加的含义应由客户代码决定。

例如,如果变量聚合结构的成员为MinimumMaximum,则该结构本身不应承诺Minimum <= Maximum。接收这样一个结构作为参数的代码,其行为应该像传递单独的MinimumMaximum值一样。要求Minimum不大于Maximum应视为要求Minimum参数不大于单独传递的Maximum参数。

有时需要考虑的一个有用模式是让一个ExposedHolder类定义如下:

1
2
3
4
5
6
class ExposedHolder<T>
{
  public T Value;
  ExposedHolder() { }
  ExposedHolder(T val) { Value = T; }
}

如果一个人有一个List>,其中someStruct是一个变量聚合结构,那么他可以做myList[3].Value.someField += 7;之类的事情,但是把myList[3].Value交给其他代码将使它得到Value的内容,而不是给它一种改变它的方法。相比之下,如果使用List,则需要使用var temp=myList[3]; temp.someField += 7; myList[3] = temp;。如果使用可变类类型,将myList[3]的内容暴露于外部代码将需要将所有字段复制到其他对象。如果使用不可变的类类型或"object-style"结构,则需要构造一个与myList[3]相似的新实例(someField不同),然后将该新实例存储到列表中。

另一个注意事项是:如果要存储大量类似的东西,最好将它们存储在可能嵌套的结构数组中,最好将每个数组的大小保持在1K到64K左右。结构数组是特殊的,在索引中,可以直接引用内部的结构,因此可以说"a[12].x=5;"。虽然可以定义类似数组的对象,但C不允许它们与数组共享这种语法。


最好的解决办法是再测量一次,再测量一次。也许有一些细节说明你在做什么可以简化,简单的答案是"Use structures"or"use classes"difficult.


好吧,如果您最终使用struct,那么去掉字符串并使用固定大小的char或byte缓冲区。

那是:表演。


从C++的角度来看,我同意与类相比,修改结构属性会慢一些。但我确实认为,由于结构是在堆栈上而不是堆上分配的,所以它们的读取速度会更快。从堆中读取数据比从堆栈中读取数据需要更多的检查。


使用阶级。

我们有通用的笔记为什么不更新B作为创建他们?