14

I researched this subject but I couldn't find any duplicate. I am wondering why you can use a struct in an array without creating an instance of it.

For example, I have a class and a struct:

public class ClassAPI
{
    public Mesh mesh { get; set; }
}

public struct StructAPI
{
    public Mesh mesh { get; set; }
}

When ClassAPI is used in an array, it has to be initialized with the new keyword before being able to use its properties and methods:

ClassAPI[] cAPI = new ClassAPI[1];
cAPI[0] = new ClassAPI(); //MUST DO THIS!
cAPI[0].mesh = new Mesh();

But this is not the case with StructAPI. It looks like StructAPI doesn't have to be initialized in the array:

StructAPI[] sAPI = new StructAPI[1];
sAPI[0].mesh = new Mesh();

If you try the same thing with ClassAPI, you would get a NullReferenceException.

Why is it different with structs when using them in an array?

I understand the difference between class and struct with struct being a value type but that still doesn't make sense. To me, without the array being involved in this, it would look like I am doing this:

StructAPI sp;
sp.mesh = new Mesh();

Notice that the sp variable is not initialized and it should result in a compile-time error that says:

Error CS0165 Use of unassigned local variable 'sp'

but that's a different story when the struct is put in an array.

Is the array initializing the struct in it? I would like to know what's going on.

Boann
  • 48,794
  • 16
  • 117
  • 146
Programmer
  • 121,791
  • 22
  • 236
  • 328
  • 3
    [Specification](https://github.com/dotnet/csharplang/blob/master/spec/variables.md#array-elements): For the purpose of definite assignment checking, an array element is considered initially assigned. – user4003407 Jul 22 '18 at 06:05
  • @PetSerAl That makes sense. So it's doing something like `StructAPI sp = default(StructAPI);` for each item in the array? If so, is it a compile or run-time thing? – Programmer Jul 22 '18 at 06:08
  • Can you use variables of other value types, like `int`, uninitialized in C#? If not, it’s the same idea. `new int[5]` is all zeros. (I used VB.NET more, where `Dim sp As StructAPI : sp.mesh = New Mesh()` would give a warning but work fine.) – Ry- Jul 22 '18 at 06:12
  • In C#, you don't have to initialize structs, but you do have to initialize classes. – Powerlord Jul 22 '18 at 06:14
  • @Ry- Yes, you can use `int` uninitialized in C# because it's a value type just like `struct` but you can't do the-same with struct. I also put the compile-time it would give you when you try to do so. – Programmer Jul 22 '18 at 06:16
  • The run-time processing of an array creation expression consists of the following steps: … All elements of the new array instance are initialized to their default values. https://github.com/dotnet/csharplang/blob/master/spec/expressions.md#array-creation-expressions – user4003407 Jul 22 '18 at 06:17
  • 1
    @Programmer: I’m very surprised to hear that. – Ry- Jul 22 '18 at 06:19
  • @Powerlord This is exactly what every SO post I found said but you do need to initialize it in order to use it. In my case you must with the `new`, `default` keyword or with reflection. Something must be assigned to it or you'll get an error mentioned in my question. – Programmer Jul 22 '18 at 06:19
  • 1
    @Programmer That is because you use properties. Property accessors are just a methods, and methods can not be called on not fully initialized struct. But fields can be assigned even if struct is not initialized. – user4003407 Jul 22 '18 at 06:30
  • @PetSerAl: How does a “not fully initialized struct” work? – Ry- Jul 22 '18 at 06:35
  • @PetSerAl *"methods can not be called on not fully initialized struct."* Not really. It works even with methods. All struct items in the array are not null. I did that test even before posting an just did it again to verify. The class items are null though(which is expected). Thanks for your links. They are very useful and helped create an answer. – Programmer Jul 22 '18 at 06:37
  • @Programmer Can you show an example where method called on not fully initialized struct? The only exception: constructor allowed to assign automatically implemented properties. That exception was added in C# 6. In C# 5 even that was not allowed. – user4003407 Jul 22 '18 at 06:49
  • @Ry- The definite assignment states of instance variables of a *struct_type* variable are tracked individually as well as collectively. … A *struct_type* variable is considered definitely assigned if each of its instance variables is considered definitely assigned. https://github.com/dotnet/csharplang/blob/master/spec/variables.md#definite-assignment – user4003407 Jul 22 '18 at 06:54
  • @PetSerAl Sorry, I misread your comment. I did not see the "not" in the first sentence in your comment but you're correct about that error with properties and fields. – Programmer Jul 22 '18 at 06:56

3 Answers3

10

As per the specification link provided by PetSerAl in the comments:

Array elements
The elements of an array come into existence when an array instance is created, and cease to exist when there are no references to that array instance.

The initial value of each of the elements of an array is the default value (Default values) of the type of the array elements.

For the purpose of definite assignment checking, an array element is considered initially assigned.

(emphasis mine).

This means that when you declare an array of T, each "cell" in the array is being initialized using default(T). For reference types default(T) returns null, but for value types default(T) returns the type default value – 0 for numbers, false for bool, and so on.

As per the Using Structs (C# Programming Guide) page:

If you instantiate a struct object using the default, parameterless constructor, all members are assigned according to their default values.

Since structs are value types, default(T) where T is a struct initializes the struct to its default value, meaning all its members will be initialized to their default values – null for reference types and whatever default value for value types.

So this line of code StructAPI[] sAPI = new StructAPI[1]; basically creates a new array of StructAPI containing a single StructAPI instance, where its mesh property is default(Mesh).

Zohar Peled
  • 79,642
  • 10
  • 69
  • 121
  • *"For reference types default(T) returns null, but for value types default(T) returns the type default value"* Didn't know this about `default` behavior with reference and value type. Although, I suspected that `default` was playing a role in this after reading [PetSerAl's](https://stackoverflow.com/questions/51462431/why-struct-in-array-dont-have-to-be-initialized#comment89893888_51462431) comment. – Programmer Jul 22 '18 at 06:33
  • 1
    Yep, that about sums that up :-) – Zohar Peled Jul 22 '18 at 06:35
4

This is likely because the fact that classes, while they do have a default constructor, structs actually don't have one.

Why class arrays need to be intialized

Classes create the object and then return the reference. The actual variable is a reference to that value. Default values are always zeros, so the default value to a reference is null, because null is represented by an address of all zeros, meaning its not pointing to anything.

Because of this, the default value for the array of a class is all null references.

Why struct arrays don't need to be

Structs, on the other hand, are all by value. They also don't have a default parameterless constructor and C# doesn't let you create one (though the CLR does). Because of the fact that there isn't a constructor, the CLR is able to very efficiently create the struct by zeroing out all of the values, without having to call the constructor.

You can view more about why this is from this StackOverflow question.

Nisarg Shah
  • 14,151
  • 6
  • 34
  • 55
AustinWBryan
  • 3,249
  • 3
  • 24
  • 42
2

When you initialize an array, default values are assigned to its elements:

  • null for reference types,
  • for value types, the default value varies: zero for types representing numbers, for struct it is little bit different, its default value is struct with all fields set to their default values. Again, for reference types it's null and for value types it depends (as mentioned above).

So, basically, when you initialize array you have your structs initialized (set to default value), that's why you can access their properties.

Michał Turczyn
  • 32,028
  • 14
  • 47
  • 69