对象作为数据成员的C#结构

对象作为数据成员的C#结构,c#,struct,thread-safety,C#,Struct,Thread Safety,正如我们所知,在C#中,结构是通过值传递的,而不是通过引用传递的。因此,如果我有一个包含以下数据成员的结构: private struct MessageBox { // data members private DateTime dm_DateTimeStamp; // a struct type private TimeSpan dm_TimeSpanInterval; // also a struct private ulong dm_MessageID;

正如我们所知,在C#中,结构是通过值传递的,而不是通过引用传递的。因此,如果我有一个包含以下数据成员的结构:

private struct MessageBox
{
    // data members
    private DateTime dm_DateTimeStamp; // a struct type
    private TimeSpan dm_TimeSpanInterval; // also a struct
    private ulong dm_MessageID; // System.Int64 type, struct
    private String dm_strMessage; // an object (hence a reference is stored here)
    // more methods, properties, etc ...
}
因此,当MessageBox作为参数传递时,会在堆栈上进行复制,对吗? 就数据成员的复制方式而言,这意味着什么? 前两个是结构类型,所以副本应该是DateTime和TimeSpan。第三种类型是原语,因此它也是复制的。但是dm_strMessage呢,它是对一个对象的引用?复制时,会创建对同一字符串的另一个引用,对吗?对象本身驻留在堆中,并且不会被复制(堆上只有一个实例)。因此,现在我们必须引用String类型的同一对象。如果这两个引用是从不同的线程访问的,那么可以想象字符串对象可能会因为同时从两个不同的方向修改而损坏。MSDN文档说明System.String是线程安全的。这是否意味着String类有一个内置机制来防止对象在这里描述的情况下被损坏? 我试图找出MessageBox结构与类相比是否存在任何潜在的缺陷/陷阱。 谢谢你的意见

Source.Energy.

字符串不能被多线程访问“破坏”,因为它们是不可变的

但是,您应该避免使结构可变。阅读和回答以了解更多信息

我试图找出MessageBox结构与类相比是否存在任何潜在的缺陷/陷阱

它可能不应该是一个结构。有关详细信息,请参阅MSDN上的指南

除非类型具有以下所有特征,否则不要定义结构:

  • 它在逻辑上表示单个值,类似于基本类型(整数、双精度等)
  • 它的实例大小小于16字节
  • 它是不变的
  • 它不必经常装箱

我认为您的MessageBox肯定违反了第一条和第二条准则,也可能违反了第三条准则,具体取决于可用的方法。

从GC的角度来看,传递单个结构与将字段作为单个参数传递没有太大的不同。当在结构中传递字符串时,与将字符串作为简单参数传递相比,您当然不需要担心什么


字符串是不可变的,因此它们不可能被破坏,不管有多少线程共享它们。

首先,您的第一句话暗示您认为类是通过引用传递的。事实并非如此——引用是按值传递的(默认情况下)。有关更多详细信息,请参阅。当你明白这一点,它可能会使其他方面更清楚

你的问题实际上是关于两件不同的事情:

  • 如何复制结构值
  • 在线程之间共享字符串有多安全
我想如果你把两者分开会对你有帮助的

复制结构的值时,无论是值类型还是引用类型,成员的处理方式都是相同的。只需逐位复制值。需要了解的重要一点是,
dmstrmessage
的值是一个引用,而不是字符串对象。该引用被复制

这并不比此代码更有害:

string message = GetMessageFromSomewhere();
string otherMessage = message;
发生了完全相同的事情:
message
的值被复制到
otherMessage
:这两个变量具有相同的值,这是对单个字符串对象的引用


到目前为止,这与线程无关。现在,如果您在多个线程之间共享一个字符串引用,这是安全的,因为字符串是不可变的。您无法更改字符串对象的内容,因此两个字符串可以很好地从同一对象读取数据,而不会有损坏的风险。对于.NET中的许多其他类型,情况并非如此。例如,在可能修改列表的多个线程之间共享一个
列表
是不安全的。

这里有一个类似的问题,最后是Jon Skeet的回答谢谢你们的回答,伙计们。你的回答为我指明了正确的方向,我发现了一些与结构和不变性相关的帖子,所以现在我认为我对这些概念的理解要好得多。还有一个值得解决的后续问题:如果在堆栈上创建结构(这是正确的,不是吗?),并且我管理结构的队列(即System.Collection.Queue),这是否意味着队列中的所有结构都存储在堆栈上?那可能不是我想要的,嗯?特别是因为其中一个数据成员是字符串,它可能会变长。总是在堆栈上创建结构是不正确的。并且,您不应该试图将对类和结构的理解建立在它们是存储在堆栈还是堆上的基础上。这是一个实现细节,将来可能会改变(但可能不会)。但是为了回答您的问题,对字符串的引用可以存储在堆栈或堆上,但是字符串中的字符数据甚至对于structs中的字符串也会存储在堆上。好的,Jon,谢谢。澄清一下:在您的示例中,变量message(String类型)是对字符串对象的引用,因此当消息作为参数传递时,引用本身被复制,这意味着它是按值传递的,正如您所说的。表示字符串对象的数据驻留在堆上,并且绝对不会被复制。所以在消息被传递之后,假设它没有超出作用域,我们最终得到了对同一个String对象的两个引用。以上所有内容是否准确无误?是的,我现在看到我最初的问题与线程/线程安全性没有太大关系。