Strange behavior of Struct in C# - c#

I have a structure as below. I have few problems
Problem1:
struct MyStruct
{
public MyStruct(int a)
{
this.a = a;
this.b = 10;
}
public int a;
public int b;
}
When I remove this.b from MyStruct constuctor it will give me an error "Field must be fully assigned before control is returned to the caller". but in case of class it doesn't occur
Problem2:
struct MyStruct
{
//public MyStruct(int a)
//{
// this.a = a;
// this.b = 10;
//}
//int asd;
//public int MyProperty { get; set; }
public void getImplemen()
{
Console.WriteLine("azsdfa");
}
public int a;
public int b;
}
static void Main(string[] args)
{
MyStruct myStruct ;
myStruct.a = 15;//when I comment this it will give an error
myStruct.b = 15; //when I comment this it will give an error
myStruct.getImplemen();
}
When I change MyStruct myStruct to MyStruct myStruct = new MyStruct ();
it works fine.
why so?

That's just how it goes.
Default constructor initializes every field to a default value, while a constructor with parameters forces you to initialize every field in the struct.
What if you have a default constructor AND one with parameters, you ask? Well, I don't remember. Easy enough to check on your own.

It does not allocate memory for fields:
MyStruct myStruct;
Allocates memory and initialize fields in constructor:
MyStruct myStruct = new MyStruct();
If you does not allocate memory for a variable then you can not assign a value to the fields. Сonstructor allocate memory and initializes fields (you need initialize fields in constructor before control is returned to the caller).

you should refer to https://msdn.microsoft.com/en-us/library/aa288471(v=vs.71).aspx
you need to create the instance of your struct before using it.

The difference is that structs are value types while classes are reference types. When a value type object is created, memory space will be allocated to store the value, thus its member variable cannot be null, whilst class member variables can be null. Hence, the compiler only complains when struct member variables are not assigned to.

Remember the thumb rule for Structs: All fields must be initialized. Values can be provided by you or default ones.
For Question 1:
When you initialize struct with 'new' (without parameters), all fields in it are initialized to default type values (0 for int, null for string etc). Since you are using parameterized constructor compiler does not use default one and hence you get error if you don't initialize field 'b'. You can still make this work as below:
public MyStruct(int a) : this()
{
this.a = a;
}
For Question 2:
Recall the thumb rule I mentioned in the beginning. So you either use default constructor with 'new' initialization or set field values in calling code.
Quick suggestion: Please do not use public fields in class/struct. Use properties to encapsulate them.

Related

C# structs documentation: relation of contructor initializers to `ref` and `out` parameters

In C# documentation and particulary Structs I found these sentences which I can't understand:
If the struct instance constructor doesn't specify a constructor initializer, the this variable corresponds to an out parameter of the struct type, and similar to an out parameter, this must be definitely assigned (Definite assignment) at every location where the constructor returns. If the struct instance constructor specifies a constructor initializer, the this variable corresponds to a ref parameter of the struct type, and similar to a ref parameter, this is considered definitely assigned on entry to the constructor body.
I want to know what is the relation between this keyword in constructors and, the ref and out keywords.
If you're not familiar with the terms used in the documentation this might seem a bit convoluted, but let's take it step by step.
Basic rule is: Struct constructors must ensure all fields of a struct instance has been initialized. No exceptions.
Also, relevant for the following is that a "constructor initializer" means you're chaining calls through multiple constructors:
public YourStruct(...) : this(...)
^----+----^
|
+-- constructor initializer
For reference, out and ref parameters to methods:
public void Test(out int x) { ... }
public void Test(ref int x) { ... }
means that the out int x parameter has to be fully initialized in the method, and is considered unassigned at the start of the method. See the out parameter modifier documentation for reference.
Whereas the ref int x parameter is considered assigned at the start of the method, and the method can change it but does not have to. See the ref keyword for more information.
So, let's take the rest of the documentation now.
If you do not have a constructor initializer for your constructor, then it means your constructor has to tackle the basic rule all by itself. This is what it means that it is similar to an out parameter for a method, your constructor has to fully initialize the struct instance before returning.
However, if you do have a constructor initializer, then that constructor also has to fully initialize the struct instance and so by the time your constructor gets to execute, the instance is already fully initialized. In this case, your constructor does not have to initialize all the fields any more but can selectively change only the fields it needs. This is what it means regarding the ref parameter.
Let me give an example:
public struct X
{
public int A;
public int B;
public X(int a, int b)
{
// no constructor initializer, MUST initialize both A and B
A = a;
B = b;
}
}
However:
public struct X
{
public int A;
public int B;
public X(int a)
: this(a, 0)
{
// constructor initializer, DOES NOT have to initialize anything
// you can, though, if you want to change B
B++;
}
public X(int a, int b)
{
// no constructor initializer, MUST initialize both A and B
A = a;
B = b;
}
}
So basically the documentation states:
If you DO NOT have a constructor initializer, the constructor has to fully initialize the this variable, similar to how an out parameter works
If you DO have a constructor initializer, the constructor DOES NOT have to do anything, and you can consider the this variable similar to a ref parameter instead.

Why the C# compiler forces explicit initialization of all value type's fields in the constructor?

For a class, this is OK:
class Point
{
private int _x, _y;
public Point(int x)
{
_x = x;
}
}
But if I use a value type the compiler complains that the _y is not initialized:
struct Point
{
private int _x, _y;
public Point(int x)
{
_x = x;
}
}
I'm wondering what's the rationale behind this? Why can't the compiler just initialize _y to 0 as it does when there is no explicit constructor is provided?
Why? Because the language specifiaction (ECMA 334 16.4.9) says so:
No instance function member (including the set accessors for the properties X and Y) can be called until all fields of the struct being constructed have been definitely assigned. Note, however, that if Point were a class instead of a struct, the instance constructor implementation would be permitted.
As for why the language specification demands it... presumably to make struct assignment as cheap as possible by avoiding an unnecessary wipe and overwrite.
This can be found in Section 5.3.1 of C# spec which states that the following types of variables are initially assigned. The list for the purposes is:
Instance variables of class instances.
Instance variables of initially assigned struct variables.
Array elements.
Value parameters.
Reference parameters.
Variables declared in a catch clause or a foreach statement.
The following will give you a value of 0:
Point p = new Point(10);
Console.WriteLine(p.y);
As the variables are initialized to their default values which can lead to bugs in code, the compiler warns on uninitialised variables.
This by itself won't compile:
int temp;
Console.WriteLine(temp);
Jon Skeet has a good answer here:
Non initialized variable in C#
You can do it like this, now all value types are initialized:
struct Point
{
private int _x, _y;
public Point(int x)
:this()
{
_x = x;
}
}
Note, I simply called the default constuctor.
I think the reason for this behavior is, that if you either call the default constructor or specify all fields in your constructor with parameters, it shows, you know what you're doing and not accidental initialize some fields to default.

Value types fields are reference types?

I am a bit confused here. With structs, I cannot directly change its fields as the whole struct is of the value type.
But what about classes with value type fields? If I have a public field int X, I can modify it properly. So the only manifestation of value-type nature of its field would be that when passed as an argument, it is a copy?
EDIT: Also
Class A
{
int B=100; //this is a field, so reference type. But it is a value type.
}
The address to content of B is stored on the heap, but is the value of B stored on the stack?
With structs, I cannot directly change its fields as the whole struct is of the value type.
You cannot change fields in situations when C# makes a copy of the struct for you. In other situations, you can modify fields of a struct. Here is a small demonstration of the difference:
struct Foo {
public int A {get;set;}
public void SetA(int a) {
A = a;
}
}
class Bar {
Foo f;
public Foo F {
get{return f;}
set {f = value;}
}
public void SetFooA(int x) {
f.SetA(x);
}
}
public static void Main() {
Bar b = new Bar();
b.F.SetA(123); // b.F makes a copy, so changing A fails
Console.WriteLine("{0}", b.F.A);
b.SetFooA(456); // Inside Bar, f.SetA is called directly, so the change works fine
Console.WriteLine("{0}", b.F.A);
b.F = new Foo { A = 112233 }; // This also works, because the whole F is assigned
Console.WriteLine("{0}", b.F.A);
}
If I have a public field int X, I can modify it properly.
The same rule applies to user-defined structs, as long as you modify the whole of it. You can't modify part of an int, because it is not a composite. Modification of a struct works fine when you assign the whole struct at once. In other words, assigning
b.F = new Foo { A = 112233 };
in my example replaces assigning
B = 100;
in your example.
Demo.
The address to content of B is stored on the heap, but is the value of B stored on the stack?
No. Value type fields of a class are stored on the heap.
Please refer to #Marc Gravell's answer here for more information about this:
Why value-types are stored onto Stacks?
You may also want to read #Eric Lippert's blog post on the subject: https://blogs.msdn.microsoft.com/ericlippert/2010/09/30/the-truth-about-value-types/
In short value types can be stored on the stack but they are not always stored on the stack.
Struct gives you a copy of the original field because a copy of the struct would be passed. Changing it to class would pass a reference to the class and therefore the field would modify if you were to change it.

Why should we initialize a struct before passing it to a function?

Consider this code:
MyStruct j;
j.I = 3;
Console.WriteLine(j.I);
Now see this method:
public static void StructMod(MyStruct ms)
{
ms.I += 100;
Console.WriteLine(ms.I);
}
When we pass MyStruct to the method, should we initialize it before passing it? I.e.:
MyStruct j = new MyStruct()
Unless struct is value type?
Yes, it is.
First line from MSDN:
A struct type is a value type...
As with individual variables, a struct must be fully initialized before it can be used. Consider:
struct Foo
{
public int Bar;
public int Pop;
}
class Program
{
static void Main(string[] args) {
Foo f;
f.Bar = 3;
test(f); // ERROR: Use of unassigned local variable 'f'
}
static void test(Foo f) {
Console.WriteLine("{0}", f.Bar);
}
}
The solution here is to either initialize .Bar, or simply call the Foo constructor f = new Foo() which initializes all members to their default values.
This is the same logic behind the "Field ... must be fully assigned before control is returned to the caller" error that you get when adding parameterized constructors to structs.
After a little testing, it seems that either way you declare it, it's being initialized.
One reason to call new ... would be to execute code in the constructor, if any.
Take this sample from MSDN, for example:
public struct CoOrds
{
public int x, y;
public CoOrds(int p1, int p2)
{
x = p1;
y = p2;
}
}
You could just do this, and use c, but then x and y will be 0.
CoOrds c;
Alternatively, you could do this, and then x and y start off with values.
CoOrds c = new CoOrds(5,3);
(This would be more useful if the ctor did something more interesting.)
If you want the struct to have a particular value, then initialize it to that value.
If you want the struct to be initialized to its default value, then you don't need to explicitly initialize it. However, it might still be best to do so for the sake of readability if the struct's default value is non-obvious.
As a side note: Mutable structs are really bad; the entire scenario you propose in your example is a code smell. See Why are mutable structs “evil”?
The same behavior could be achieved more safely with an immutable struct like so
struct MyStruct
{
public readonly int I;
public MyStruct(int i_initial)
{
I = i_initial;
}
}
MyStruct j = new MyStruct(3);
public static void StructMod(MyStruct ms)
{
Console.WriteLine(ms.I + 100);
}
In particular, note that from the caller's point of view this function's behavior will be exactly the same as the one in the question, because structs are passed by value the same way that primitives like int are. If the "Mod" in StructMod is there because you expect this function to modify the value, then that's a good concrete example of why it's generally better for mutable types to be objects and not structs.

Why does setting the property in the constructor of a struct not work?

I have following code which is not allowed (error below), why?
struct A
{
private int b;
public A(int x)
{
B = x;
}
public int B
{
get { return b; }
set { b=value; }
}
}
I receive the following error:
The 'this' object cannot be used before all of its fields are assigned
to Field 'Test.x' must be fully assigned before control is returned
to the caller
A struct's variables all have to be definitely assigned before you can use any methods or properties. There are two possible fixes here:
1) You can explicitly call the parameterless constructor:
public A(int x) : this()
{
B = x;
}
2) You can use the field instead of the property:
public A(int x)
{
b = x;
}
Of course the second option only works in your current form - you have to use the first option if you want to change your struct to use an automatic property.
However, importantly, you now have a mutable struct. This is almost always a very bad idea. I would strongly urge you to use something like this instead:
struct A
{
private readonly int b;
public A(int x)
{
b = x;
}
public int B { get { return b; } }
}
EDIT: More details of why the original code doesn't work...
From section 11.3.8 of the C# spec:
If the struct instance constructor doesn't specify a constructor initializer, the this variable corresponds to an out parameter of the struct type
Now initially that won't be definitely assigned, which means you can't execute any member function (including property setters) until all the firsts of the struct being constructed have been definitely assigned. The compiler doesn't know or try to take account of the fact that the property setter doesn't try to read from another field. It's all in aid of avoiding reading from fields which haven't been definitely assigned.
Change your constructor to:
public A(int x) :
this()
{
B = x;
}
As to "why", refer to 11.3.8 Constructors and 5.3 Definite assignment.

Categories

Resources