views:

294

answers:

3

As we know, in C# structs are passed by value, not by reference. So if I have a struct with the following data members:

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 ...
}

So when a MessageBox is passed as a parameter, a COPY is made on the stack, right? What does that mean in terms of how the data members are copied? The first two are struct types, so copies should be made of DateTime and TimeSpan. The third type is a primitive, so it's also copied. But what about the dm_strMessage, which is a reference to an object? When it's copied, another reference to the same String is created, right? The object itself resides in the heap, and is NOT copied (there is only one instance of it on the heap.) So now we have to references to the same object of type String. If the two references are accessed from different threads, it's conceivable that the String object could be corrupted by being modified from two different directions simultaneously. The MSDN documentation says that System.String is thread safe. Does that mean that the String class has a built-in mechanism to prevent an object being corrupted in exactly the type of situation described here? I'm trying to figure out if my MessageBox struct has any potential flaws / pitfalls being a structure vs. a class. Thanks for any input.

Source.Energy.

+3  A: 

Strings cannot be "corrupted" by multithreaded access because they are immutable.

You should avoid making your structs mutable though. Read this question and answers for more information.

I'm trying to figure out if my MessageBox struct has any potential flaws / pitfalls being a structure vs. a class.

It probably should not be a struct. See the guidelines on MSDN for choosing between a class and a struct.

Do not define a structure unless the type has all of the following characteristics:

  • It logically represents a single value, similar to primitive types (integer, double, and so on).
  • It has an instance size smaller than 16 bytes.
  • It is immutable.
  • It will not have to be boxed frequently.

I think your MessageBox definitely breaks the first and second guidelines, and possibly also the third depending on what methods are available.

Mark Byers
Thank you for your replies, guys. Your answers pointed me in the right direction, and I found *several* posts related to structures and immutability, so now I think that I understand the Concepts much better.There is one more follow-up question worth addressing: If structures are created on the stack (that's correct, isn't it?), and I manage a Queue (i.e. System.Collection.Queue) of Structures, does that mean that ALL of the structures in the queue are stored on the stack? That's probably not what I want, huh? Especially since one of the data members is a String, which could grow long.
It is not correct that structures are always created on the stack. And you should try not to base your understanding of classes and structs on whether they are stored on the stack or the heap. This is an implementation detail and could change in the future (but probably won't). But to answer your question, the *reference* to a string can be stored either on the stack or the heap, but the *character data* in a string will be on the heap even for strings in structs.
Mark Byers
A: 

From a GC perspective, passing a single struct is not much different to passing the fields as individual parameters. There's certainly nothing to concern yourself with when passing a string in a struct, vs. passing a string as a simple parameter.

Strings are immutable, so they are impossible to corrupt, no matter how many threads share them.

Marcelo Cantos
+1  A: 

Firstly, your first sentence implies that you think classes are passed by reference. This isn't the case - the reference is passed by value (by default). See my article on parameter passing for more details. When you understand this, it may make other aspects clearer.

Your question is really about two different things:

  • How struct values are copied
  • How safe it is to share strings between threads

I think it will help you if you separate the two.

When copying the value of a struct, the members are treated the same way whether they're value types or reference types. The value is simply copied, bit for bit. The important thing to understand is that the value of dm_strMessage is a reference, not a string object. That reference is copied.

This is no more harmful than this code:

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

Exactly the same thing happens: the value of message is copied into otherMessage: the two variables have the same value, which is a reference to a single string object.

So far, this has nothing to do with threading. Now if you share a string reference between multiple threads, that's safe - because strings are immutable. You can't change the contents of a string object, so two strings can perfectly happily read data from the same object with no risks of corruption. The same is not true of many other types in .NET. For example, it's not safe to share a List<T> between multiple threads which are potentially going to modify the list.

Jon Skeet
Yes, that's all correct
Jon Skeet