Why doesn't a struct in an array have to be initialized? - c#

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.

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).

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.

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.

Related

What exactly is new [] doing in a class vs struct?

MyClass[] CLASS = new MyClass[5];
int[] STRUCT = new int[5];
What exactly is new [] doing for a class vs a struct. Apparently the struct has some overloaded static index that causes it to run the default constructor of the struct. However the new [] for a class appears to do nothing but make space to initialize an instance of a class. How do I overload the static behavior of the class to also run the default constructor. I know how to use for loops and other methods of accomplishing this. My question is very specific to what is going on underneath the new []. I understand that a struct needs a default value. But doesn't a non-nullable class also need a default value which is why it gives an error when you try to use it? Or is this telling me that all classes are actually nullable?
No, the default constructor of struct isn't run. What happens instead is that in the struct case, the memory is initialized to zero. What this means depends on the data type. E.g., a reference field (like a class or a string) becomes null, numeric fields become 0, boolean fields become false, etc.
This differs very much from how classes work. This is why with the initialization of the class array, you see null values. Basically, it comes down to this. An "empty" class variable (or array in your case) becomes null. However, when you have an "empty" struct, you already have something valid. However, it is initialized as empty.
The easiest way to see this is when you e.g. have an int field in a class. This works basically the same way. When you add an int field to a class, you are not required to initialize it. When you don't initialize it, it gets the value 0 by default. struct's work the same way in this regard.
See http://msdn.microsoft.com/en-us/library/aa664471.aspx for a bit more information.
A few more things to note (answer to the question in the comments):
Classes are always nullable. This means that the only way to initialize the array is to create a loop and initialize a new instance for every item in the array;
The default constructor doesn't apply to structs, because they aren't allowed to have a default constructor. If you try to compile the following code snippet, you will get a compilation error telling you this:
struct MyStruct
{
public MyStruct()
{
}
}

New-ing string types

From Pro C#
Referring to "New-ing" Intrinsic data types...
All intrinsic data types support what is known as a default constructor. It allows you to create a variable using the new keyword.
[...] Object references (including strings) are set to null.
In C#, strings do not have a public default constructor. My guess is that due to string's immutability, they have a private default constructor. But, the context here is talking about Object references and string's as a whole while using new.
Because one cannot do
String myString = new String();
So,
String a;
referencing string doesn't result in a "default value". Instead, it's a compiler error to access a.
Though
public class StringContainer
{
public static string myString { get; set; }
}
Results in a legally accessible string (defaulted to null). This doesn't use new. It performs some kind of magical construction.
What is occurring in the StringContainer scenerio? Because there appears to be no new-able default constructor in string, is this an error in the C# book?
All intrinsic data types support what is known as a default constructor. It allows you to create a variable using the new keyword.
There are an impressive number of subtle errors in that statement.
First off, there is no such thing as an "intrinsic" data type; perhaps this term is defined somewhere else in the book?
Second, it would be more accurate to say that all struct types have a public parameterless constructor called the "default" constructor. Some class types also have a public parameterless ctor; if you do not provide any ctor then the C# compiler will automatically generate a public parameterless ctor for you. If you do provide a ctor then the C# compiler will not do this for you.
Third, constructors do not create variables. The author is conflating a bunch of related but different things: the "new" operator, the memory manager, the constructor and the variable, and the created object. Variables are storage locations and are managed by the CLR; they are not created by the "new" operator.
The correct statement is that the "new" operator on a struct causes a variable to be created by the CLR on the temporary storage pool; that variable is then initialized by the memory manager, and then passed to the constructor for more initialization. The value thus created is then copied somewhere else. The "new" operator on a class causes the CLR to create an object on the long-term storage pool, and then pass a reference to that object to the CLR. No "variable" need be involved.
Confusing variables with objects is a very common error; it is valuable to understand the difference.
In C#, strings do not have a public default constructor.
Correct.
My guess is that due to string's immutability, they have a private default constructor.
Good guess, but wrong. There is no private parameterless constructor on string.
[an auto-property or field of type string] results in a legally accessible string (defaulted to null). This doesn't use new. It performs some kind of magical construction.
It does no such thing. A null reference is not a constructed object at all. It's the absence of a constructed object!
You're basically saying that my empty garage contains a "magically constructed" non-existing car. That is an exceedingly weird way to look at an empty garage; an empty garage contains no car at all, not a magically constructed non-existing car.
What is occurring in the StringContainer scenerio?
The containing type contains a compiler-generated field -- a variable -- of type string. Let's suppose the containing type is a struct or class. When the storage for the struct or class is initialized by the memory manager, the memory manager writes a null reference into the storage location associated with the variable.
Finally: I suspect your confusion is because you've gotten the "default constructor" and the "default value of a type" confused. For a struct, they are the same thing:
int x = new int();
and
int x = default(int);
both make an int initialized to zero.
For a class, they do not do the same thing:
Fruit f = new Fruit();
makes a new fruit reference and assigns the reference to variable f, whereas:
Fruit f = default(Fruit);
is the same as
Fruit f = null;
No constructor is called.
All intrinsic data types support what is known as a default constructor. It allows you to create a variable using the new keyword.
I'm not sure what the author means by "intrinsic data types". My best guess is that he actually means "value types" (i.e. types declared with C#'s struct keyword), because value types always have a default constructor, and reference types may not.
So if you have a field whose type is a struct type (e.g. Int32, CancellationToken), then that field will be initialized as if the type's default constructor was called.
In actual implementation, there's probably not an actual call to the type's default constructor -- the memory is just initialized to all zeroes, which is the same thing that would happen if you did call the default constructor. (That's why you can't provide your own parameterless constructor for a value type -- the parameterless constructor always initializes the memory to all zeroes. This greatly simplifies things like new int[10000] -- the compiler doesn't actually have to call new Int32() 10,000 times; it just zeroes out the memory.)
Your question about a string field in a class isn't really related to the author's discussion of "intrinsic data types", because both string and your enclosing class are reference types, not value types. So your class won't have a parameterless-constructor-that-you-can't-override; it will just have normal constructors. But the zeroing-out behavior is still there: when you call a constructor, the new memory block is zeroed out before the constructor code starts to run. Your string field is a reference type, and a zero reference is null.
I imagine it is using default(string) which returns null because string is a reference type.
Also, remember that constructors can't return null.

C#: What data types require NEW to allocate memory?

I want to understand better the difference between using 'new' to allocate memory for variables and the cases when new is not required.
When I declare
int i; // I don't need to use new.
But
List<string> l = new List<string>();
Does it make sense to say "new int()" ?
You will need to use new to allocate any reference type (class).
Any value type (such as int or structs) can be declared without new. However, you can still use new. The following is valid:
int i = new int();
Note that you can't directly access a value type until it's been initialized. With a struct, using new TheStructType() is often valuable, as it allows you full use of the struct members without having to explicitly initialize each member first. This is because the constructor does the initialization. With a value type, the default constructor always initializes all values to the equivalent of 0.
Also, with a struct, you can use new with a non-default constructor, such as:
MyStruct val = new MyStruct(32, 42);
This provides a way to initialize values inside of the struct. That being said, it is not required here, only an option.
Any reference type (such as classes) will require new. Value types (such as int) are simple values and do not require new.
You do not need to new value types in c#. All other types you do.
Have look to this MSDN documentation on new
It is also used to invoke the default constructor for value types, for example:
int myInt = new int();
In the preceding statement, myInt is initialized to 0, which is the default value for the type int. The statement has the same effect as:
int myInt = 0;
int i is value type that's why you don't need to initialize and new List<string>() is reference type, you need to assign an object instance to it
Reference types must be allocated using new.
Value types do not have to be heap allocated. Integer, Double, and struct types are examples of value types. A value type that is a local var will be stored on the function call stack. A value type that is a field of a class will be stored in the class's instance data.
Simply check the IL: you can see the compiler emits either an 'initobj' or 'newobj'.
The initobj is emitted for both int i = 0; and int i = new int();
http://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.initobj(v=vs.85).aspx

C# - 2 struct questions

I know structs are value types but then I do not understand why this works:
EDIT: I mean, why this.Size.Height does not work then?
struct A
{
int height;
public int Height
{
get
{
return height;
}
set
{
height = value;
}
}
}
//... class Main
{
A a = A();
a.Height = 5; //works. Why? I thought it should say "cannot modify as it is not variable". I think the properties should return copy of this struct...?
}
Second question - I have read I do not need to use "new" with structs but it does not work without it for me.
Let me break that down into several questions:
What is a variable?
A variable is a storage location that contains a value.
Why are value types called value types?
The value of a variable of value type is a value, and is copied by value. The value of a variable of reference type is a reference and is copied by reference. That is why value types are called value types and reference types are called reference types.
Why does a.Height = 10 work?
To change the value stored in a variable of reference type, you've got to have a variable to begin with. In this case, you do: you have the variable "a". The compiler compiles that as "pass the managed address of the variable 'a' to the Height setter with the argument 10". Therefore the Height property setter knows how to find the location of the value stored in 'a' and mutate it.
Why does a.Size.Height = 10 not work?
To change the value stored in a variable of reference type, you've got to have a variable to begin with. The expression "a.Size" is not a variable; it is a value. a.Size does not give you the variable that backs the property -- in fact, there might not be one. Instead, it gives you the value of the property. Value types are copied by value; this is a copy of the value of the property. This means that the compiler has two choices: it can copy the value into a temporary variable and then mutate that variable, tricking you into thinking that you've mutated the backing store of a.Size. Or, it can give you an error indicating that you're doing something foolish. It does the latter.
Isn't this confusing and vexing?
Yes. The moral of the story is do not make mutable value types. Only make immutable value types. Never have a setter on a value type in the first place; only do the assignment in the constructor. If the thing has to be mutable, make it a reference type.
Do I have to use "new" to make a new instance of a value type?
No. You can also use "default":
Foo f = default(Foo);
If Foo is a value type then this fills in the contents of storage location f with the Foo which has all its fields set to their default values.
Or, if the value type is mutable, you can simply set the values of all the fields. However, you have to set all of them if you do not use a constructor or the default initializer. You have to set all of them including private fields.
But if a struct has all public fields doesn't that violate best practices guidelines in two ways? First, because it has public fields, and second, because it is a mutable value type?
Yes. Don't do that.
I think you are confusing value types with immutability. I think this SO question will help you.
"this.Size.Height = 5" does not work because when a value type is used as the type of a property, the above line of code would actually mean "this.get_Size().set_Height(5)", and the result of the get_Size() call is a copy of the original, being a value type.
Thus, were it allowed by C#, setting the property value to 5 would change the copy's value rather than the original property value, which is highly undesirable.
Of course, this does not apply when the value type property of a class is changed via a local variable, so this scenario can be safely be supported.
It is very normal. And why you think that it must not let you to set value for Height ?
It is very normal behavior how properties should work.
Concerning calling new, yes it is not mandatory for value types. For value types it just calls default constructor which just initializes fields with default values.
A property will return the value that its "get" method returns. It's the same for structs and classes. If you provider a "set" method, it will do whatever the "set" indicates. The only way that Height could not be modified would be either to use a private "set", or not provide a "set" at all.

c# in VS2005: what do the following types default to in C#?

For C# in VS2005, what will be value of variables of the following types if they are simply declared and not assigned to any value? ie. What are their default values?
int
bool
string
char
enum
Here's what default value for each type you've mentioned would be.
int = 0
bool = false
string = null
char = '\0'
enum = 0 //behind the scenes enum is int
Taking this forward, at runtime if you wish to capture default value of any type then you can use default statement in C# and simply call it as following.
//This will print 0 on screen.
Console.WriteLine(default(int));
Generally, this is used in generics for identifying default values of generic type arguments, where the type is only known at runtime.
If they're used locally (i.e. they're not members of a class or struct) they won't have default values. You won't be able to use them until they're assigned a value (unless you explicitly "new them up").
If they're not used locally, they'll default to 0, false, null, and '\0'.
Edit: You've added enum to your list. enums default to a value of 0 because they use an int by default behind the scenes. So whatever enumerate is declared as 0 for the enum (typically the first enumerate, but that's overridable) will be the default. If you don't have a 0 value enumerate for whatever reason, then you'll have an invalid value for your enum as the default value.
Please explain. I am talking about declaring the variables inside the scope of a class or method. – Craig Johnston
I told a little white lie to point out how the compiler works. If you declare a local variable, the compiler will not compile if you try to use it without having first explicitly assigning a value to that variable; that includes null for reference types.
For example:
public void Foo()
{
int bar;
int barPlus5 = bar + 5; // Compiler Error!
}
Technically, bar still has a default value of 0, but because the C# compiler will not allow you to use that default value in a locally scoped variable, a locally scoped variable effectively doesn't have a default value. <Ben Kenobi>So what I told you was true, from a certain point of view.</Ben Kenobi>.
There are exceptions to the rule: out parameters get a pass because the compiler enforces that an out parameter must be assigned by a method before it returns, and you can do int bar = new int(); to get the default value, since that's technically an assignment.
Now, if you declare a variable as a member of a class or a struct such as follows:
public class Foo
{
public int Bar {get;set;}
public Foo() { }
}
And then you instantiate Foo somewhere, Bar will have a default value of 0.
For example:
var foo = new Foo();
Console.WriteLine(foo.Bar); // output: 0
MSDN article - Value Types.
All value types implicitly declare a
public parameterless instance
constructor called the "default
constructor". The default constructor
returns a zero-initialized instance
know as the default value for the
value type.
In case of local variables (Value or
Reference Types) in C#, they must be
initialized before they are used.
MSDN Article - Types
A type that is defined as a class,
delegate, array, or interface is a
reference type. At run time, when you
declare a variable of a reference
type, the variable contains the value
null until you explicitly create an
instance of the object by using the
new operator, or assign it an object
that has been created elsewhere by
using new.

Categories