Why are we not forced to instantiate a struct, like when using a class?
-
2There are exceptions. You are required to use 'new', if you have a constructor with a parameter in the struct. – thewpfguy Mar 16 '13 at 09:22
-
1@thewpfguy: In C#, `myVar=new MyStruct(5)`;` is roughly equivalent to either `MyStruct temp; myStruct.ctor(out temp, 5); myVar = temp;`, or `myStruct..ctor(ref myVar, 5);`, with the compiler using the latter (which is faster) when it *thinks* it's equivalent to the former. In vb.net, it's equivalent to `myVar = new MyStruct; myStruct..ctor(myVar, 5);`. The `new` syntax isn't responsible for create the struct instance in `myVar` in any case. – supercat Apr 24 '13 at 15:42
-
@ColonelPanic `default(T)` – nawfal Jul 03 '16 at 20:26
7 Answers
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.

- 40,828
- 14
- 81
- 115

- 647,829
- 179
- 1,238
- 2,067
-
I understand that when creating e.g. an array of some value type, or a struct or class with a value-type field, all items of the array, class, structure would exist before the constructor could be called on any of them. On the other hand, I'm curious what fundamental difficulties would exist, if any, with allowing structures to declare constant field initialization values? All that would mean would be that instead of filling the structure with zero, it would copy a constant template. For example, a field with legit values -1000000000 to 1000000000 could be initialized to -2147483648. – supercat Oct 14 '11 at 16:09
-
Amazing answer. I made it an answer [here](http://stackoverflow.com/a/17009725/661933) as well (hopefully with your permission). – nawfal Jun 09 '13 at 12:45
-
Should we, when possible, skip the constructor call to avoid the short-term allocation and copy? – fibriZo raZiel Nov 19 '15 at 11:56
-
1Can i assume that 'long term' means heap and 'short term' means stack? – James Poulose Dec 07 '15 at 20:16
-
4@JamesPoulose: An implementation of C# or the CLR is not *required* to use a heap for long term storage, nor is it required to use a stack for short term storage. For example, some short term storage is stored in registers; registers are neither heap nor stack. Why would you make that assumption? More to the point: what piece of code are you planning on writing which is *wrong* if that assumption is violated? – Eric Lippert Dec 07 '15 at 23:07
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.

- 1,026,079
- 266
- 2,566
- 2,900
-
3The memory will be zeroed automatically, whether or not you invoke a constructor. `new StructType()` is the same as `default(StructType)`, by definition. "because the spec says so" is not really the reason. The important info to take from the spec is that the memory is automatically zeroed. – Zenexer Apr 23 '13 at 21:31
-
1@Zenexer can you point me to a specification reference for that? For fields: sure - but not for structs generally; they have the same rules of definite assignment, but with a special case that if you write to all the fields separately that also counts as assigned. At the lower levels, there is a flag that controls whether the stack space of a method is zeroed; currently the compiler *does* set this flag, but that is an implementation detail (not a language one); the rules of definite assignment mean that it isn't strictly necessary – Marc Gravell Apr 24 '13 at 08:13
-
Just test whether the two are equal in a sandbox program. I'm not quite sure for which part you want a specification reference; I'd just point you to structs. That's how structs are described to work. – Zenexer Apr 26 '13 at 19:30
-
@MarcGravell: All struct members must be definitely assigned before the struct can be passed to external code as an `out` parameter (since external code may regard `out` and `ref` parameters as equivalent). C# has to ensure that any members that aren't otherwise written before a struct is passed as an `out` parameter will be zeroed out, since there's no guarantee that external code won't try to read them. – supercat Jun 17 '13 at 22:04
-
Because a struct is a value-type. When you declare a variable of it, the instance is immediateley there.
A constructor (the new
operator) is therefore optional for a struct.
Consider
struct V { public int x; }
class R { public int y = 0; }
void F()
{
V a; // a is an instance of V, a.x is unassigned
R b; // b is a reference to an R
a.x = 1; // OK, the instance exists
//b.y = 2; // error, there is no instance yet
a = new V(); // overwrites the memory of 'a'. a.x == 0
b = new R(); // allocates new memory on the Heap
b.y = 2; // now this is OK, b points to an instance
}

- 263,252
- 30
- 330
- 514
-
2well, only *partially* there; if you added `Console.WriteLine(a.x);` ***above*** the `a.x = 1;` line, it wouldn't compile. – Marc Gravell Oct 14 '11 at 12:45
-
1@Marc: correct but not directly related to what I wanted to say. `a.x` exists but in not definitely assigned. – H H Oct 14 '11 at 12:46
-
I dont know if this is because perhaps a later version of c# but.... your line 1 does not compile, cannot have instance properties or field initializers in structs – Stark Mar 06 '19 at 12:28
-
1@Stark - not sure if this ever worked, I will just remove the `= 0;`. `a.x` will start as 'not definetly assigned'. – H H Mar 07 '19 at 07:24
Because structs are value types and classes are reference types. So structs fall into the same category as int, double etc.

- 601,492
- 42
- 1,072
- 1,490
Coming in a year and a half later...
A struct
is usually passed by value, while a class
is always passed by reference. You probably have a good understanding of what happens when an object is passed by reference. When an object is passed by value, its contents, rather than a reference to the object, are passed along. To the programmer, it appears as though a shallow copy of the object is made. Changing one instance won't change the other.
All variables (including fields and properties) always have space allocated for them as long as they exist. It's important to note that local variables don't exist until a value is assigned to them in newer versions of C#. In the case of class
-type variables, the allocated space will contain a reference to the contents of the object. In the case of a struct
-type variable, the allocated space will contain the actual contents of the object.
So, let's say you have a an "empty" class
-type variable. It will have a default reference. That reference will equate to null
. However, a struct
-type variable isn't a reference: it's the actual contents of an object. When left "empty", all of its fields (and auto-implemented properties, which are backed by fields behind-the-scenes) all contain default values--in other words, they are also "empty". If they are reference types, they'll be null
; if they are value types, they'll be 0, or zeroed structs (and the chain continues).
This is also why a structs
cannot have a default constructor. Just as you cannot override what a class
looks like when it is null
, you cannot override what a struct
looks like when it is zeroed.
There is an underused operator for obtaining the default value of any type--class
, struct
, or intrinsic. That is the default()
operator. For example:
class ClassType { }
struct StructType { }
//
// ...
//
var classA = default(ClassType);
var classB = (ClassType)null;
if (classA == classB)
{
// This will execute, because both equal null.
}
var structA = default(StructType);
var structB = new StructType();
if (structA == structB)
{
// This will execute, because both are zeroed.
}
//
// ...
//
/// <summary>
/// An example use case for the <c>default()</c> operator.
/// </summary>
/// <returns>
/// <c>null</c> if <c>T</c> is a reference type, a zeroed instance <c>T</c> is a
/// <c>struct</c>, or <c>0</c> if <c>T</c> is an intrinsic type.
/// </returns>
private static T GetDefault<T>()
{
// This line wouldn't compile, because T could be a value type.
//return null;
// This line wouldn't compile, because T could be a reference type without a default or accessible constructor.
//return new T();
// This will work!
return default(T);
// In newer versions of C#, when the type is known from the context, it can be omitted:
//return default;
}

- 18,788
- 9
- 71
- 77
-
I really dislike the phrase "classes are 'passed' by reference". Class-type values can be passed by value or by ref just like those of structs; what's different is that a struct-type value *is* an instance, while a class-type value is an instance-identifier. The statement `foo = new bar[3];` creates three new spaces holding values of type `bar`. If `bar` is a struct type, the value in each space will be a default-valued instance. If it's a class type, the value in each space will be a null instance-identifier. – supercat Oct 18 '13 at 16:45
-
@supercat It's typical to say that they're "passed by reference" versus "passed by value". Standard programmer jargon. That being said, you're on the right track. Instantiation of a class results in a reference, whereas instantiation of a struct results in a value. Let's not muck up the waters with arrays: `new Bar[3]` does not necessarily create three spaces; it just appears to do so. That's getting into the internals of CLR. `stackalloc Bar[3]` definitively creates a contiguous space for 3 Bar values, provided Bar is a struct. – Zenexer Oct 25 '13 at 05:43
-
If `Bar` is a class, then `New Bar[3]` defines space to hold three *references*. That's not an "implementation detail"--that's what it does. Whether those three references refer to three distinct objects at any given time depends whether references to three distinct objects are stored in them. – supercat Oct 26 '13 at 04:50
-
@supercat Functionally, yes. In implementation, not necessarily. For example, a language could support sparse arrays, and many do. You're thinking of lower-level languages like C/++. – Zenexer Oct 30 '13 at 01:58
-
While `New Bar[1048576]` need not physically *allocate* memory to hold 1048576 object references, it *semantically defines* 1024 new storage locations which previously did not exist. The act of constructing the array need not allocate the backing storage, but it defines every slot in the array, such that two methods which access element 12345 of that array will access the same storage location, and a method which accesses element 54321 will see a different one. – supercat Oct 30 '13 at 15:09
-
Even if internally the system couldn't handle arrays larger than 65536 elements, so the array was really a `Bar[16][]` which would lazily allocate inner arrays, every array element *semantically exists*. If two threads do `Interlocked.CompareExchange(ref theArray[65538], new Bar(), null)`, even if the backing storage for `theArray[65538]` doesn't initially exist, the standard guarantees that both `CompareExchange` operations will access the same storage location (i.e. one will succeed and one will fail). – supercat Oct 30 '13 at 15:18
-
@supercat No, they won't access the same memory location, because that memory location doesn't exist. Concurrency/locking in .NET is much more complex than that. – Zenexer Nov 13 '13 at 06:21
-
I didn't say *memory location*. I said *storage location*. The .NET framework doesn't require that thread 1 regards `theArray[65538]` as occupying the same memory address as thread 2 does, but if two threads perform a `CompareExchange` on the same *storage location*, the spec requires the method to regard one as having happened before the other, and the second one must see the value written by the first one. The most practical way to implement that it to use the same physical address, but that's not the only way. The system could store for each thread a list of every array slot it wrote... – supercat Nov 13 '13 at 15:53
-
...and have the `CompareExchange` in one thread lock the array and scan all threads' lists of writes to see if the element it's writing has been written by anyone else. Probably not a very practical implementation, but it would be legal. The key thing is that `new Bar[1048576]` creates a new identifier of some sort and do whatever it must so that a `CompareExchange` on one thread will see which indices have had `CompareExchange` operations performed by another thread. – supercat Nov 13 '13 at 16:00
As said by David Heffernan and Henk Holterman its because structs are value types and hence instantiates while declaring it. For a better understanding about ValueType and ReferenceType, please check this link P Daddy has nicely explained it.

- 1
- 1

- 3,248
- 4
- 35
- 48
Additionally to what was posted: Note that a struct cannot have a parameterless constructor or have a initializer for any of its instance fields. The default value is having all value type fields set to their default value (ex. 0 for ints, false for bool, etc.) and all reference type fields to null.
Secondly, a struct is initialized, for example by calling a constructor or using default()
.

- 17,233
- 9
- 65
- 88
-
1Bzzz true in C#, false in .NET http://msmvps.com/blogs/jon_skeet/archive/2008/12/10/value-types-and-parameterless-constructors.aspx The important quote: `Yes, you can write a parameterless constructor for a value type in .NET` – xanatos Oct 14 '11 at 12:50
-