1) Why must initialization value of field members be determinable at compile time?
2) But if the initialization value needs to be determined at compile time, then why am I able to initialize a A.b field using a reference to an object:
class A
{
B b = new B();
}
class B {}
Thank you
I'm a bit confused; there is no such rule for fields. In particular, fields are explicitly initialized anyway - either by your code, or to the type's default. The only corner-case is explit struct field initialization, in which all fields must be assigned before the struct is considered complete.
I wonder if you are talking about "definite assignment" of variables within methods; i.e.
void Foo() {
int i;
Console.WriteLine(i); // not valid; i not assigned
i = 1;
Console.WriteLine(i); // would be value
}
This is so that for method variables the values never suffer from random values picked up from the stack. Actually, there is an IL marked that means that locals are wiped - and IIRC the C# compiler includes this marker anyway... but the language spec says method variables must be definitely assigned.
In the case of you example, that field-initialization essentially becomes part of the common constructor code. But you'd never notice it.
1) Why must initialization value of field members be determinable at compile time?
It mustn't. Why do you think that ?
If you mean this part of the book Illustrated C# 2008, then it is in the section talking about const fields. The compiler may replace the use of a const field with the value of the const field, so the initialiser for the field must be constant.
The field in your original post is not a const. Therefore the restriction does not apply to it.
Related
struct Mutable {
private int x;
public Mutable() { } //Compiler Error CS0171
}
So if i declare a constructor for a struct, It says that all the fields must be assigned but I am thinking why cant't the inline initializer just DEFAULT the field' values to their defaults?
I mean when no Constructor is specified the inline initializer does default the fields' values to their defaults.
Actually it can, but it is planned for the next language version - C# 11. From the docs:
Auto-default struct
The C# 11 compiler ensures that all fields of a struct type are initialized to their default value as part of executing a constructor. This change means any field or auto property not initialized by a constructor is automatically initialized by the compiler. Structs where the constructor doesn't definitely assign all fields now compile, and any fields not explicitly initialized are set to their default value. You can read more about how this change affects struct initialization in the article on structs.
Read more:
Proposal
What does happen behind the scenes when you make a struct without using the new keyword?
Let's say we have this struct:
struct Person
{
public int Age;
public string Name;
}
And In the Main() method I decide to make an instance of it without the new keyword like that:
Person p;
now if I try to access p.Age I will get a compile-time error saying "Use of possibly unassigned field 'Age'" however if I make an instance of the struct like that:
Person p = new Person();
and then I try to access p.Age I will get the value of 0. Now what exactly happens behind the scenes? Does the Runtime initialize these variables for me or the compiler places code that initializes them in the IL after compilation?
Edit:
Can anybody also explain this behavior:
Code:
struct Person
{
public string Name { get; set; }
}
If I make instance of struct like that:
Person p;
and I initialize the name manually
p.Name = "SomeRandomName"';
I won't be able to use it. The compiler gives an error "Use of an unassigned local variable p" but If I make instance of the struct with the default (parameterless) constructor there isn't such an error.
Members don't have the same rules as locals.
Locals must be explicitly initialised before use. Members are initialised by the runtime to their respective default values.
If you want some more relevant information:
In the internal implementation details (non-contractual!), up to the current MS .NET runtime for Windows objects are allocated in pre-zeroed memory on the heap (when they're on the heap at all, of course). All the default values are "physical" zeroes, so all you need is e.g. "200 consecutive bytes with value 0". In many cases, this is as simple as asking the OS for a pre-zeroed memory page. It's a performance compromise to keep memory safety - you can easily allocate an array of 2000 Person instances by just doing new Person[2000], which just requests 2000 * size of Person bytes with value zero; extremely cheap, while still keeping safe default values. No need to initialise 2000 Person instances, and 2000 int instances and 2000 string instances - they're all zero by default. At the same time, there's no chance you'd get a random value for the string reference that would point to some random place in memory (a very common error in unmanaged code).
The main reason for requiring explicit initialisation of locals is that it prevents stupid programming errors. You should never access an uninitialised value in the first place, and if you need a default value, you should be explicit about it - the default value then gets a meaning, and meanings should be explicit. You'll find that cases where you could use an uninitialised local meaningfully in the first place are pretty rare - you usually either declare the local right where it gets a value, or you need all possible branches to update a pre-declared local anyway. Both make it easier to understand code and avoid silly mistakes.
If you go through the small struct documentation, you can quote:
A struct type is a value type that is typically used to encapsulate small groups of related variables, such as the coordinates of a rectangle or the characteristics of an item in an inventory.
Normally, when you declare in your code these value type like:
int i; // By default it's equal to 0
bool b; // by default it's equal to false.
Or a reference type as:
string s; //By default it's null
The struct you have created is a value type, which by default isn't initialized and you can't access its properties. Therefore, you can't declare it as:
Person p;
Then use it directly.
Hence the error you got:
"Use of possibly unassigned field 'Age'"
Because p is still not initialized.
This also explains your second part of the question:
I won't be able to use it. The compiler gives an error "Use of an unassigned local variable p" but If I make instance of the struct with the default (parameterless) constructor there isn't such an error.
The same reason you couldn't directly assign p.Name = "something" is because p is still not initialized.
You must create a new instance of the struct as
Person p = New Person(); //or Person p = default(Person);
Now, what happens when you create a new instance of your struct without giving values to the struct properties? Each one of them will hold it's default value. Such as the Age = 0 because it's an int type.
Every datatype in .NET has a default value. For all reference types it's null. For the special type string it is also null. For all value types it is something akin to zero. For bool it's false because this is the equivalent to zero.
You can observe the same behaviour when you write a class with member fields. After construction all these fields will have a default value even when none was assigned during construction.
The same is also true when you use a struct as a member. Since a struct cannot be null, it will also be initialized and all its members (again) use their default values.
The difference in compiler output is that the compiler cannot determine if you have initialized the member field through any means. But it can determine if you have set the method variable value before reading it. Technically this wouldn't be necessary but since it reduces programming errors (why would you read a variable you have not written?), the compiler error appears.
Ok this is more curiosity than practical requirement.
Let's say I have this class:
public sealed class Entity
{
int value;
Entity()
{
}
public static implicit operator Entity(int x)
{
return new Entity { value = x };
}
}
I don't want this class to be instantiated outside the class and this works. But here Entitys will be referenced and not copied like value types. This class being so small I want it to behave likes value types. Also if I compare two instances of Entitys like e1 == e2 it's going to give reference equality (ok I can overload == but that's more work). So I would make it a struct:
public struct Entity
{
int value;
public static implicit operator Entity(int x)
{
return new Entity { value = x };
}
}
But now someone can do new Entity() easily.
My question is there a way I can have a type (struct/class) that
1) will be copied every time the value is accessed, so that
Entity e1 = 1;
Entity e2 = e1;
ReferenceEquals(e1, e2); // prints false, just like any value type
2) has value semantics when comparing for equality
Entity e1 = 1;
Entity e2 = 1;
e1.Equals(e2); // prints true
// this is easy though by overriding default equality.
3) is not instantiable outside the scope of the type:
Entity e = new Entity(); // should not compile
Basically close to an enum's behaviour. I can certainly live without it, just learning.
Its not possible to do re-define or try to hide the default constructor for a struct in C# because the C# compiler routinely comples code based on the assumption that the parameterless constructor does nothing. See Why can't I define a default constructor for a struct in .NET?.
Your options are either
Use a class
Ensure that the default value for your struct has some meaning (e.g. in your case the int will be initialised to 0) - this is what enums do (recall that enums are initialised to 0).
My recommendation would be to use a class until you identify that there is a performance problem.
A type has no say over what can happen to storage locations of that type. Storage locations of any type can come into existence, or have their contents overwritten with the content of other locations of the same type, without the type itself having any say in the matter. A storage location of a reference type holds a class reference; when it comes into existence, it will be a null reference. The only way a storage location of a reference type will hold anything other than a null reference is if an instance of that type is created and--outside some scenarios involving Full Trust Code and reflection--a reference type will have full control over the circumstances where that can occur.
By contrast, a storage location of a value type is an instance of that type. Consequently, there is no way a structure can control how one comes into existence. What a structure can control--to a limited extent--is what values its fields can contain. In limited trust scenarios, a private struct field can only assume a non-default value if the code for the struct writes that value to some instance of the struct (if the value gets written to any instance of the struct, even limited-trust code with access to that instance may be able to use multi-threaded overlapped reads and writes to produce a struct with a combination of field values which the struct itself would never create itself).
I would suggest creating a struct with a single private field of a class type. You cannot prevent code from creating a default(Entity), but that doesn't mean you have to allow code to do anything useful with one. Its Equals method should work well enough to return true when comparing to another default instance, and false when compared with anything else, and GetHashCode should return the same value for all default instances, but any other methods could throw an exception.
Make all of the constructors (besides the static constructor) private for the class Entity. protected would work too, but since its sealed, that doesn't make a lot of sense.
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.
I would like to try this code:
public struct Direction
{
private int _azimuth;
public int Azimuth
{
get { return _azimuth; }
set { _azimuth = value; }
}
public Direction(int azimuth)
{
Azimuth = azimuth
}
}
But it fails on compilation, I understand that struct need to init all its fields.
but i am trying to understand what happens under the CLR\IL hoods.
why it need all the fields before any other method\property\this etc.
Thanks.
Value Types are created on the stack (unless nested within a reference type) There is something about fields/locations on the stack that the CLR can't guarantee that they will be zeroed out (contrary to fields/locations on the managed heap which are guaranteed to be zeroed out). Hence they must be written to before being read. Otherwise it's a security hole.
The struct's default ctor (which takes no parameters and which you're not allowed to explicitly specify) zeroes out all fields of a struct and hence you can use a struct after you do.
new BimonthlyPairStruct()
However when you implement your parameterized ctor, you must ensure that all fields have been initialized - which is required for the CLR to pass your code as safe/verified .
See Also: CLR via C# 2nd Ed - Pg 188
I just found an explanation in the MSDN forum stating that this rule is enforced because zeroing out the memory is skipped if you use a none default constructor. So you will have to provide initialization values for all fields in order to avoid some fields containing random values. You achieve this easily be calling the parameter less default constructor, but at the cost of initializing some fields twice.
I cannot tell if this explanation is correct, but it sounds reasonable.
When you define a non-default initializer, C# requires you to set all fields because it
skips the zeroing of memory and lets you initialize it - otherwise you'd have to have a
double initialization performance hit. If you don't care about the (very slight)
performance hit you can always chain a call to the : this() initializer and then only
initialize selected fields.
This is because structs are derived from System.ValueType and not System.Object, System.ValueType implements default constructur which you cannnot override, this default constructer initializes all fields in struct with its default value. So if you are implementing any parameter contructor in your class you will also need t0 ensure you invoke system.ValueType default const. And to answer why it needs to init all its value this is because value are stored in stack memory.
This works:
public Direction(int azimuth)
{
_azimuth = azimuth;
}
From the spec:
Struct constructors are invoked with
the new operator, but that does not
imply that memory is being allocated.
Instead of dynamically allocating an
object and returning a reference to
it, a struct constructor simply
returns the struct value itself
(typically in a temporary location on
the stack), and this value is then
copied as necessary.
Basically, the compiler must see that every field gets initialized in the constructor so that it can copy those values, and it is not willing to examine calls to functions or properties.
public struct Direction
{
public int Azimuth { get; private set; }
public Direction(int azimuth) : this()
{
Azimuth = azimuth;
}
}
You need to initialize the field, and not via the property.