Why is it possible to instantiate a struct without the new keyword?

Why are we not forced to instantiate a struct with “new”, like when using a class?

When you “new” a reference type, three things happen. First, the memory manager allocates space from long term storage. Second, a reference to that space is passed to the constructor, which initializes the instance. Third, that reference is passed back to the caller.

When you “new” a value type, three things happen. First, the memory manager allocates space from short term storage. Second, the constructor is passed a reference to the short term storage location. After the constructor runs, the value that was in the short-term storage location is copied to the storage location for the value, wherever that happens to be. Remember, variables of value type store the actual value.

(Note that the compiler is allowed to optimize these three steps into one step if the compiler can determine that doing so never exposes a partially-constructed struct to user code. That is, the compiler can generate code that simply passes a reference to the final storage location to the constructor, thereby saving one allocation and one copy.)

So now we can address your question, which you actually have asked backwards. It would be better to ask:

Why are we forced to allocate a class with “new”, instead of simply being able to initialize the fields as with a struct?

You have to allocate a class with “new” because of those three things on the list. You need new memory allocated from the long-term storage and you need to pass a reference to that storage to the constructor. “new” is the operator that knows how to do that.

You don’t have to call “new” on a struct because there is no need to allocate the “final” storage; the final storage already exists. The new value is going to go somewhere, and you already have obtained that storage by some other means. Value types do not need a new allocation; all they need is initialization. All you need to do is ensure that the storage is properly initialized, and you can often do that without calling a constructor. Doing so of course means that you run the risk of having a variable of value type that can be observed to be in a partially initialized state by user code.

Summing up: calling a ctor is optional for value types because no new memory needs to be allocated when initializing an instance of a value type and because skipping the constructor call means that you get to skip a short-term allocation and a copy. The price you pay for that performance gain is that user code can see a partially initialized structure.

The why is simply – because the spec says so. The how is a matter of ensuring that the entire block of memory is “definitely assigned”, which means: assigning a value to each field of the struct. However, this requires 2 nasty things:

  • public fields (almost always bad)
  • mutable fields (generally bad in a struct)

so in most best-practice cases, you do need to use the new(...) syntax, to invoke the constructor (or to zero-the memory, for the parameterless constructor) for the type correctly.

Leave a Comment