What (if any) are the implications of having an object or a nullable type as a field in a struct
For performance reasons I use structs in several use cases.
If I have an object or a nullable type (another struct but nullable) as a member in the struct, is there an adverse effect on performance. Do I lose the very benefit I am try开发者_高级运维ing to gain?
Edit
I am aware of the size limitations and proper use of structs. Please no more lectures. In performance tests the structs perform faster.
I do not mean to sound abrasive or ungrateful, but how do I make my question any more simple?
Does having a object as a member of a struct impact performance or negate the benefit?
Well, C# is a strange beast when it comes to the performance part of struct vs classes.
Check this link: http://msdn.microsoft.com/en-us/library/y23b5415(VS.71).aspx
According to Microsoft you should use a struct only when the instance size is under 16 bytes. Andrew is right. If you do not pass around a struct, you might see a performance benefit. Value type semantics have a heavy performance (and at time memory, depending on what you are doing) penalty while passing them around.
As far as collections are concerned, if you are using a non-generic collection, the boxing and unboxing of a value-type (struct in this case) will have a higher performance overhead than a reference type (i.e. class). That said, it is also true that structs get allocated faster than classes.
Although struct and class have same syntax, the behavior is vastly different. This can force you to make many errors that might be difficult to trace. For example, like static constructors in a struct would not be called when you call it's public (hidden constructor) or as operator will fail with structs.
Nullable types are themselves are implemented with structs. But they do have a penalty. Even every operation of a Nullable type emit more IL.
Well, in my opinion, struct are well left to be used in types such as DateTime or Guids. If you need an immutable type, use struct otherwise, don't. The performance benefits are not that huge. Similarly even the overhead is not that huge. So at the end of day, it depends on your data you are storing in the struct and also how you are using it.
No, you won't lose the benefit necessarily. One area in which you see a performance benefit from using a struct is when you are creating many objects quickly in a loop and do not need to pass these objects to any other methods. In this case you should be fine but without seeing some code it is impossible to tell.
Personally, I'd be more worried about simply using structs inappropriately; what you have described sounds like an object (class) to me.
In particular, I'd worry about your struct being too big; when you pass a struct around (between variables, between methods, etc) it gets copied. If it is a big fat beast with lots of fields (some of which are themselves beasts) then this copy will take more space on the stack, and more CPU time. Contrast to passing a reference to an object, which takes a constant size / time (width per your x86/x64 architecture).
If we talk about basic nullable types, such as classic "values"; Nullable<T>
of course has an overhead; the real questions are:
- is it too much
- is it more expensive than the check I'd still have to do for a "magic number" etc
In particular, all casts and operators on Nullable<T>
get extra code - for example:
int? a = ..., b = ...;
int? c = a + b;
is really more similar to:
int? c = (a.HasValue && b.HasValue) ?
new Nullable<int>(a.GetValueOrDefault() + b.GetValueOrDefault())
: new Nullable<int>();
The only way to see if this is too much is going to be with your own local tests, with your own data. The fact that the data is on a struct in this case is largely moot; the numbers should broadly compare no matter where they are.
Nullable<T>
is essentially a tuple of T
and bool
flag indicating whether it's null
or not. Its performance effect is therefore exactly the same: in terms of size, you get that extra bool
(plus whatever padding it deems required).
For references to reference types, there are no special implications. It's just whatever the size of an object reference is (which is usually sizeof(IntPtr)
, though I don't think there's a definite guarantee on that). Of course, GC would also have to trace through those references every now and then, but a reference inside a struct
is not in any way special in that regard.
Neither nullable types nor immutable class types will pose a problem within a struct. When using mutable class types, however, one should generally try to stick to one of two approaches:
- The state represented by mutable class field or property should be the *identity*, rather than the *mutable charactersitics*, of the mutable object referenced thereby. For example, suppose a struct has a field of type `Car`, which holds a reference to a red car, vehicle ID #24601. Suppose further that someone copies the struct and then paints the vehicle referred to blue. An object reference would be appropriate if, under such circumstances, one would want the structure to hold a reference to a blue car with ID #24601. It would be inappropriate if one would want the structure to still hold a refernce to a red car (which would have to have some other ID, since car ID #24601 is blue).
- Code within the struct creates a mutable class instance, performs all mutations that will ever be performed to that instance (possibly copying data from a passed-in instance), and stores it in a private field after all mutations are complete. Once a reference to the instance is stored in a field, the struct must never again mutate that instance, nor expose it to any code which could mutate it.
Note that the two approaches offer very different semantics; one should never have a hard time deciding between them, since in any circumstance where one is appropriate the other would be completely inappropriate. In some circumstances there may be other approaches which would work somewhat better, but in general one should identify whether a struct's state includes the identity or mutable characteristics of any nested mutable classes, and use one of the patterns above as appropriate.
精彩评论