C#struct将object作为数据成员

时间:2010-05-02 06:48:35

标签: 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; // System.Int64 type, struct
    private String dm_strMessage; // an object (hence a reference is stored here)
    // more methods, properties, etc ...
}

因此,当MessageBox作为参数传递时,堆栈上会产生COPY,对吗? 这对数据成员的复制方式意味着什么? 前两个是结构类型,因此副本应该由DateTime和TimeSpan组成。第三种类型是基元,因此它也被复制。但是dm_strMessage呢,它是对象的引用?当它被复制时,会创建对同一个String的另一个引用,对吧?对象本身驻留在堆中,并且不被复制(堆上只有一个实例。)所以现在我们必须引用String类型的同一对象。如果从不同的线程访问这两个引用,则可以想象String对象可能会被同时从两个不同的方向修改而被破坏。 MSDN文档说System.String是线程安全的。这是否意味着String类有一个内置机制来防止对象在这里描述的情况类型中被破坏? 我试图弄清楚我的MessageBox结构是否有任何潜在的缺陷/缺陷是结构与类。 感谢您的任何意见。

Source.Energy。

3 个答案:

答案 0 :(得分:4)

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

你应该避免使你的结构变得可变。阅读this question和答案以获取更多信息。

  

我正在试图弄清楚我的MessageBox结构是否有任何潜在的缺陷/缺陷是结构与类。

它可能不应该是一个结构。请参阅有关choosing between a class and a struct的MSDN指南。

  

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

     
      
  • 它逻辑上表示单个值,类似于基本类型(整数,双精度等)。
  •   
  • 它的实例大小小于16个字节。
  •   
  • 这是不可改变的。
  •   
  • 不必频繁装箱。
  •   

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

答案 1 :(得分:3)

首先,你的第一句话意味着你认为类是通过引用传递的。情况并非如此 - 引用按值传递(默认情况下)。有关详细信息,请参阅my article on parameter passing。当你理解这一点时,它可能会使其他方面更加清晰。

你的问题实际上是两件事:

  • 如何复制struct值
  • 在线程之间共享字符串的安全性

我认为如果你将两者分开,它会对你有帮助。

复制结构的值时,无论它们是值类型还是引用类型,都会以相同的方式处理成员。简单地复制该值,一点一点地进行。要理解的重要一点是dm_strMessage的值是引用,而不是字符串对象。该引用被复制。

这不比这段代码更有害:

string message = GetMessageFromSomewhere();
string otherMessage = message;

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

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

答案 2 :(得分:0)

从GC的角度来看,传递单个结构与将字段作为单个参数传递没有太大区别。在结构中传递字符串时,与将字符串作为简单参数传递相比,当然没有什么值得关注的。

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