Home > Mobile >  CS8983 A 'struct' with field initializers must include an explicitly declared constructor
CS8983 A 'struct' with field initializers must include an explicitly declared constructor

Time:08-12

struct vvvv 
{
    public int j = 8;

    //public vvvv() { } error    
}

class cccc 
{
    public int f = 8;
}

In the struct if I comment out the constructor the compiler tells me that the field j will not be initialize until I specify an EXPLICIT constructor while in the case of a class, the initializer will perfectly run before the IMPLICIT constructor's body is run.

I mean the struct also has an implicit constructor. Why do I have to specify an explicit one for the initializer to run? Isn't an implicit constructor enough?

CodePudding user response:

First of all as this answer explains (in the edit part):

The parameterless constructor isn't created by the compiler. Value types don't have to have constructors as far as the CLR is concerned - although it turns out it can if you write it in IL. When you write "new Guid()" in C# that emits different IL to what you get if you call a normal constructor.

I.e. the following:

Console.WriteLine(typeof(Struct).GetConstructors().Length); // prints 0
Console.WriteLine(typeof(Class).GetConstructors().Length); // prints 1

struct Struct
{
}

class Class
{
}

(note that for struct Struct{ public Struct() { } } the code above will print 1)

So one of the reasons can be performance an backward compatibility.

Also there are some notes on the topic in the LDM:

We feel that a simple rule here would be to mirror the observed behavior with reference types: if there is no explicit constructor for a struct type, we will synthesize that constructor. If that constructor happens to be empty (as it would be today in a constructorless struct type because field initializers aren't yet supported) we optimize that constructor away. If a constructor is explicitly declared, we will not synthesize any constructor for the type, and field initializers will not be run by new S() unless the parameterless constructor is also explicitly declared. This does have a potential pit of failure where users would expect the parameterless constructor to run the field initializers, but synthesizing a parameterless constructor would have bad knock-on effects for record structs with a primary constructor: what would the parameterless constructor do there? It doesn't have anything it can call the primary constructor with, and would result in confusing semantics.

Which resulted in the following added to the drat specification:

An error is reported if a struct has field initializers and no declared instance constructors since the field initializers will not be run.

And

If no parameterless instance constructor is declared, the struct (see §15.4.9) ...

implicitly has a parameterless instance constructor which always returns the value that results from setting all value type fields to their default value and all reference type fields to null.

Which results in this quite noteworthy behaviour:

Console.WriteLine(new V().j);  // prints 8
Console.WriteLine(new V1().j); // prints 0 - no custom parameterless ctor, no init

struct V
{
    public V() { }
    public V(int _) {}

    public int j = 8;
}

struct V1
{
    public V1(int _) { }

    public int j = 8;
}

TL;DR

C# compiler does not generate default ctor for structs, so if there is non declared - the field initializers will not run, so they are essentially pointless and to prevent such unexpected behaviour compiler does not allow field initializers without declaring ctor (not necessarily default one).

  • Related