2

Microsoft has the following rules for using struct:

Consider defining a structure instead of a class if instances of the type are small and commonly short-lived or are commonly embedded in other objects.

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

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

As far as I understand, you create struct when you want a value-type behavior. Of course this gives you copying overhead when assigning and passing it to functions. But why would you follow #2 and #3? What if your value type is just too large? Moreover, I don't understand why you would ever make an immutable type to be a value type. If the type is immutable, you would better save time for passing it by reference, since anyway it cannot be changed.

The reason that I'm asking is that I'm making a game, where objects have properties like Coordinates, Velocity etc. of the type Vector2d. The question is, should I make Vector2d immutable struct (wouldn't that require tons of additional memory?), mutable struct (people say they are evil) or just class (I will have to always call vector.Clone() because otherwise I may unintentionally get two objects having same vector variable)

Georgii Oleinikov
  • 3,865
  • 3
  • 27
  • 27
  • 2
    _"What if your value type is just too large?"_ according to MS, you should use a *class*... – gdoron Dec 22 '12 at 22:21
  • 1
    [Why are mutable structs evil?](http://stackoverflow.com/questions/441309/why-are-mutable-structs-evil) – GSerg Dec 22 '12 at 22:21
  • 3
    [You should only ask practical, answerable questions based on actual problems that you face.](http://stackoverflow.com/faq#dontask) So what is your actual question? – Erik Philips Dec 22 '12 at 22:25
  • 3
    @ErikPhilips the question is answerable. He's asking about questions about items 2 and 3 and the consequences of violating 3. If you've ever needed value type semantics then this is relevant. It may not have any context, but it is widely applicable. – Dave Hillier Dec 23 '12 at 00:27
  • Why would using structs require loads of extra memory? Does it matter if there is sharing between two objects. – Dave Hillier Dec 23 '12 at 01:29
  • @Dave it does matter because at a times I initialize positions of different objects with the same vector, though objects continue to move independently – Georgii Oleinikov Dec 23 '12 at 01:53
  • possible duplicate of [When to use struct in C#?](http://stackoverflow.com/questions/521298/when-to-use-struct-in-c) – Kate Gregory Dec 23 '12 at 02:54
  • Structure methods which modify `this` will be ineffective if applied to read-only struct instances. For that reason, you should avoid having structs *mutate themselves*. A belief that no data type should have public fields has led to a conclusion that structs should be immutable. A much more useful approach, however, is to recognize that a *struct* property which mimics a field offers none of the benefits of class parties, but slows down program execution much more. Structs with exposed fields don't have the same problems as structs with struct-mutating properties. – supercat Dec 23 '12 at 17:58
  • @gdoron: If one needs value semantics, and a data type will often be piecewise-modified (such modifications occurring, say, 10% or more of the time that instances of a struct are passed around) an exposed-field struct can be *much* more efficient than an immutable class or a so-called "immubable" struct, and *the bigger the struct, the bigger the efficiency advantage*. If one needs a data type that holds 16 values of type `Double`, a so-called "immutable" struct would have to copy all items at least once if not twice any time one was changed; an immutable class would... – supercat Dec 23 '12 at 19:15
  • ...add to that the overhead of allocating a new class object. By contrast, if the data is in an exposed-field struct that's stored in a normal variable, field, or array slot, the appropriate field can be modified without having to touch the rest of the structure. If one passes the struct by `ref` whenever practical, the cost of such passing will be independent of the size of a struct. Passing by value will be expensive, but making a new instance to hold a slightly-modified instance of a class object is far worse. – supercat Dec 23 '12 at 19:18

4 Answers4

2

Moreover, I don't understand why you would ever make an immutable type to be a value type.

The int type is an immutable type and is a perfect example of why you need immutable value types.

Imagine if int were a reference type. It would be very expensive if you had to dereference every time you used an integer. This is in fact what happens when you use "boxed" integers (e.g. integers that are stored in a variable of type object). One of the improvements in .NET compared to Java is that collections can hold unboxed integers.

If the type is immutable, you would better save time for passing it by reference

Yes if it's large and immutable then you would save time by passing it by reference. That is why the guidelines suggest that large types should be reference types.

Mark Byers
  • 811,555
  • 193
  • 1,581
  • 1,452
  • If one has an `int[1]` containing the value 0xBABE1234 one one thread uses `BlockCopy` to overwrite the first byte with 0xCE while the other uses `BlockCopy` to ovewrite the second byte 0xFA, such that the array element now holds 0xBABEFACE, what has one done to the first array element, if it wasn't mutated? A values like 0xBABEFACE may be immutable, but a value type doesn't really hold a value; it holds a collection of bytes and defines how those bytes may be interpreted as a value. – supercat Feb 09 '13 at 23:23
1

16 bytes seems somewhat arbitrary but probably relates to the cache line size for a typical CPU. This is quite a bit smaller than a 64 byte cache line on modern CPUs. Possibly because you can't explicitly align your struct to a line.

Keeping you type immutable again avoids the need to pass by reference. Instead just return a new instance. Following references is far more costly than using cached values, keeping the object small will increase the chances of it remaining in cache.

Generally speaking, if you want reference semantics make it a class otherwise make it a struct. I would recommend you follow MS guidelines unless you can prove using a profiler (or some other empirical evidence) that there is a good reason to do otherwise.

Dave Hillier
  • 18,105
  • 9
  • 43
  • 87
  • 1
    The .net framework optimizes the copying of structures 16 bytes and smaller, such that copying a 17-byte struct will be significantly slower than copying a 16-byte one. Nonetheless, if a type should exhibit value semantics and will often be piecewise-modified, an exposed-field struct, used properly, can often offer better performance than any other type. In some scenarios (e.g. piecewise modifications are nearly as frequent--if not more so--than value copies) exposed-field structs of *any size* will offer better performance than classes; the bigger the struct, the bigger the difference. – supercat Dec 23 '12 at 19:23
0

For #2, big structures should not be easily copied, and as such should be a class most of the time. Also, these are not hard rules, just rules of thumb. If you have a good reason to not follow a rule, go ahead.

-1
  1. When passing around / manipulating a struct, it's entire value has to be copied around the stack instead of just a reference. Once it reaches a certain size, 16 bytes, consider just using a class to avoid the overhead.

  2. Immutability is more of a pattern designed to reduce errors in code, not a hard and fast rule. If it's possible for your value type to be immutable, it can be helpful to make it so.

You might be misunderstanding immutability. Your variables can change values, but the values themselves don't change. For example, You can have a Datetime variable and set it to any other DateTime instance, but you can't actually change the fields of an instance after it has been initialized.

Erix
  • 7,059
  • 2
  • 35
  • 61
  • If a type should offer value semantics, then any reference-type implementation must be immutable. If one has an instance and wishes to have one that's almost identical except that `Bar` will equal 6 rather than 5, one must create a new instance which holds a copy of all the data from the old one (except for `Bar`, which should be 6 in the new one). The idea that value types should be immutable stems from problems in the way value-type methods are applied to read-only instances. The good solution is not to make so-called "immutable" value types (which really aren't), but... – supercat Dec 23 '12 at 19:28
  • ...instead recognize that if a type like `Sphere3d` is supposed to represent a quartet of `float` values, (one each for `X`, `Y`, `Z`m and `Radius`) the type should just expose those values as fields; any code which uses an instance of that type should perform the same validation as if it had received those parameters separately. Needless wrapping of things in properties can cause significant slowdowns, which get worse as structs get bigger, and is responsible for the dodgy semantics that led some people to put forth the notion that structs should be immutable. – supercat Dec 23 '12 at 19:36