300

In .NET, a value type (C# struct) can't have a constructor with no parameters. According to this post this is mandated by the CLI specification. What happens is that for every value-type a default constructor is created (by the compiler?) which initialized all members to zero (or null).

Why is it disallowed to define such a default constructor?

One trivial use is for rational numbers:

public struct Rational {
    private long numerator;
    private long denominator;

    public Rational(long num, long denom)
    { /* Todo: Find GCD etc. */ }

    public Rational(long num)
    {
        numerator = num;
        denominator = 1;
    }

    public Rational() // This is not allowed
    {
        numerator = 0;
        denominator = 1;
    }
}

Using current version of C#, a default Rational is 0/0 which is not so cool.

PS: Will default parameters help solve this for C# 4.0 or will the CLR-defined default constructor be called?


Jon Skeet answered:

To use your example, what would you want to happen when someone did:

 Rational[] fractions = new Rational[1000];

Should it run through your constructor 1000 times?

Sure it should, that's why I wrote the default constructor in the first place. The CLR should use the default zeroing constructor when no explicit default constructor is defined; that way you only pay for what you use. Then if I want a container of 1000 non-default Rationals (and want to optimize away the 1000 constructions) I will use a List<Rational> rather than an array.

This reason, in my mind, is not strong enough to prevent definition of a default constructor.

Community
  • 1
  • 1
Motti
  • 110,860
  • 49
  • 189
  • 262
  • 6
    +1 had a similar problem once, finally converted the struct into a class. – Dirk Vollmar Dec 02 '08 at 12:46
  • 4
    The default parameters in C#4 cannot help because `Rational()` invokes the parameterless ctor rather than the `Rational(long num=0, long denom=1)`. – LaTeX Feb 06 '11 at 15:05
  • SO what if you want to declare and use a complex value type that is a value in the DDD sense (i.e., immutable, and instantiate-able only through a static factory)? Is the only option to use a class instead? – Charles Bretana Sep 11 '14 at 22:06
  • 7
    Note that in ***C# 6.0*** which comes with Visual Studio 2015 it will be allowed to write zero-parameter instance constructors for structs. So `new Rational()` will invoke the constructor if it exists, however if it does not exist, `new Rational()` will be equivalent to `default(Rational)`. In any case you are encouraged to use the syntax `default(Rational)` when your want the "zero value" of your struct (which is a "bad" number with your proposed design of `Rational`). The default value for a value type `T` is always `default(T)`. So `new Rational[1000]` will never invoke struct constructors. – Jeppe Stig Nielsen Nov 20 '14 at 09:51
  • 8
    To solve this specific problem you can store `denominator - 1` inside the struct, so that the default value becomes 0/1 – miniBill Aug 19 '15 at 14:04
  • 5
    `Then if I want a container of 1000 non-default Rationals (and want to optimize away the 1000 constructions) I will use a List rather than an array.` Why would you expect an array to invoke a different constructor to a List for a struct? – mjwills Oct 18 '17 at 02:06
  • @mjwills [`List`'s constructor](https://msdn.microsoft.com/en-us/library/dw8e0z9z.aspx) doesn't create its elements it only reserves the space for elements to be created. – Motti Oct 18 '17 at 11:25
  • 2
    Doesn't that constructor create an array under the covers? https://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs `_items = new T[capacity];` In which case you are back at square one? – mjwills Oct 18 '17 at 11:48
  • @mjwills you appear to be right – Motti Oct 18 '17 at 12:58
  • @JeppeStigNielsen I tried a zero-parameter constructor on c# 6 and the code did not compile either – ihebiheb Oct 09 '18 at 20:16
  • @JeppeStigNielsen Still getting this error on VS2017. – codesniffer Nov 07 '18 at 00:53
  • 1
    @codesniffer (and ihebiheb), After I wrote the above comment, it was decided not to allow this in C# 6.0 after all. Jon Skeet mentions this in the top of his answer below. It is also not allowed in the later versions C# 7.0, 7.1, 7.2, and 7.3. And I do not think it is planned for C# 8.0 either. Read more in the thread [Parameterless constructors in structs for C# 6](https://stackoverflow.com/questions/31063109/). – Jeppe Stig Nielsen Nov 07 '18 at 20:07
  • 2
    In C# 10 this is now allowed, and there are changes in meaning when assigning struct variables with keywords "default" vs "new()". – ActiveX Nov 11 '21 at 22:59

13 Answers13

219

Note: the answer below was written a long time prior to C# 6, which is planning to introduce the ability to declare parameterless constructors in structs - but they still won't be called in all situations (e.g. for array creation) (in the end this feature was not added to C# 6)... but then it was added in C# 10 - but there are limitations, so you can't expect the constructor to run in every situation.


EDIT: I've edited the answer below due to Grauenwolf's insight into the CLR.

The CLR allows value types to have parameterless constructors, but C# doesn't. I believe this is because it would introduce an expectation that the constructor would be called when it wouldn't. For instance, consider this:

MyStruct[] foo = new MyStruct[1000];

The CLR is able to do this very efficiently just by allocating the appropriate memory and zeroing it all out. If it had to run the MyStruct constructor 1000 times, that would be a lot less efficient. (In fact, it doesn't - if you do have a parameterless constructor, it doesn't get run when you create an array, or when you have an uninitialized instance variable.)

The basic rule in C# is "the default value for any type can't rely on any initialization". Now they could have allowed parameterless constructors to be defined, but then not required that constructor to be executed in all cases - but that would have led to more confusion. (Or at least, so I believe the argument goes.)

EDIT: To use your example, what would you want to happen when someone did:

Rational[] fractions = new Rational[1000];

Should it run through your constructor 1000 times?

  • If not, we end up with 1000 invalid rationals
  • If it does, then we've potentially wasted a load of work if we're about to fill in the array with real values.

EDIT: (Answering a bit more of the question) 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. See this SO question for a bit more on that aspect.

I suspect that there aren't any value types in the framework with parameterless constructors. No doubt NDepend could tell me if I asked it nicely enough... The fact that C# prohibits it is a big enough hint for me to think it's probably a bad idea.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 9
    Shorter explanation: In C++, struct and class were just two sides of the same coin. The only real difference is one was public by default and the other was private. In .Net, there is a much greater difference between a struct and a class, and it's important to understand it. – Joel Coehoorn Dec 02 '08 at 14:01
  • 45
    @Joel: That doesn't really explain this particular restriction though, does it? – Jon Skeet Dec 02 '08 at 14:36
  • 8
    The CLR does allow value types to have parameterless constructors. And yes, it will run it for each and every element in an array. C# thinks this is a bad idea and doesn't allow it, but you could write a .NET language that does. – Jonathan Allen Dec 03 '08 at 19:55
  • 1
    @Grauenwolf: I've managed to get a value type with a parameterless constructor to compile with ilasm, but the constructor isn't being run when I initialize an array. Is there anything special I'd need to put in the IL other than taking out the parameter from a parameterful constructor (cont) – Jon Skeet Dec 03 '08 at 20:13
  • (That's how I got the IL in the first place - a C# struct with a parameterful constructor.) I'll edit my answer to explain the bit we've got to so far... – Jon Skeet Dec 03 '08 at 20:14
  • 1
    My information is based on "Framework Design Guidelines" 2nd edition. Upon rereading it, I think I may be wrong about it running the constructor for every slot in the array. – Jonathan Allen Dec 03 '08 at 20:52
  • 1
    That would certainly explain it :) I'll edit the answer. Thanks so much for correcting me though - that's a really good catch! – Jon Skeet Dec 03 '08 at 21:18
  • 1
    @JonathanAllen: FYI, there's a .NET method which will run a defined default constructor upon every slot in an array; I think there may have been an intention to have languages call this method when creating new arrays of structure types, but that philosophy has since been abandoned. – supercat Apr 29 '14 at 14:06
  • @JonSkeet, forgive me for my ignorance, but could you please elaborate on this: The basic rule in C# is "the default value for any type can't rely on any initialization". Aren't the usual value types (int, float etc) assigned default values by calling their value type ctors? – Kakira May 03 '14 at 02:09
  • 1
    @Kakira: No, they're assigned default values by wiping the memory, basically. The default value is always "what you'd get by wiping memory". You can't provide any explicit implementation which will always be called. – Jon Skeet May 03 '14 at 06:01
  • 3
    Sorry, I am a bit confused with the following. Does `Rational[] fractions = new Rational[1000];` also waste a load of work if `Rational` is a class instead of a struct? If so, why do classes have a default ctor? – kiss my armpit Jun 13 '14 at 06:16
  • 5
    @FifaEarthCup2014: You'd have to be more specific about what you mean by "waste a load of work". But when either way, it's not going to call the constructor 1000 times. If `Rational` is a class, you'll end up with an array of 1000 null references. – Jon Skeet Jun 13 '14 at 06:18
  • Sorry. Why does `class` have to have a default parameterless constructor if no ctor is explicitly defined by us as programmers? – kiss my armpit Jun 15 '14 at 21:55
  • @FifaEarthCup2014: Ah, I see. That's a decision by the C# designers - it's not required by the CLR. I believe it was intended to make development simpler, by providing you with some code by default. Personally I think it was probably a slightly bad idea, but that's a different matter. (It follows Java's lead here - Java behaves the same way.) – Jon Skeet Jun 16 '14 at 05:47
  • @JonSkeet: Now would probably be a good time to review this answer for C# 6.0's expected inclusion of parameterless constructors for structs. [New Features in C# 6 on MSDN](http://blogs.msdn.com/b/csharpfaq/archive/2014/11/20/new-features-in-c-6.aspx) – Jean Hominal Nov 26 '14 at 09:50
  • 1
    @JeanHominal: I'll add a note to say that things may change for C# 6 - I think there's just about enough chance that it will change between now and release that I'd rather not be too definite though. – Jon Skeet Nov 26 '14 at 09:51
  • _The parameterless constructor isn't created by the compiler_. [The documentation](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/using-constructors) says **"structs cannot contain an explicit parameterless constructor because one is provided automatically by the compiler."** Is the documentation incorrect or has this changed since this answer? – Saeb Amini Oct 10 '19 at 00:05
  • 2
    @SaebAmini: The documentation is incorrect, although being very precise about this in the docs may make it harder to understand. In most cases you can think about it *as if* the compiler created a constructor, but it's not actually there in the IL. – Jon Skeet Oct 10 '19 at 06:21
  • Since C# 10, you can define the parameterless constructor https://stackoverflow.com/a/76934065/8810311 – Ramil Aliyev 007 Aug 19 '23 at 07:42
  • 1
    @RamilAliyev007: Added a link at the start. – Jon Skeet Aug 19 '23 at 07:59
53

A struct is a value type and a value type must have a default value as soon as it is declared.

MyClass m;
MyStruct m2;

If you declare two fields as above without instantiating either, then break the debugger, m will be null but m2 will not. Given this, a parameterless constructor would make no sense, in fact all any constructor on a struct does is assign values, the thing itself already exists just by declaring it. Indeed m2 could quite happily be used in the above example and have its methods called, if any, and its fields and properties manipulated!

Tarik
  • 79,711
  • 83
  • 236
  • 349
user42467
  • 1,941
  • 1
  • 13
  • 8
  • 3
    Not sure why someone voted you down. You appear to be the most correct answer on here. – pipTheGeek Dec 03 '08 at 20:42
  • 16
    The behaviour in C++ is that if a type has a default constructor then that is used when such an object is created without an explicit constructor. This could have been used in C# to initialize m2 with the default constructor which is why this answer isn't helpful. – Motti Dec 04 '08 at 08:46
  • I'm not really sure what you're looking for in an answer. I don't believe there is an overriding technical limitation, I believe it was a judgement call, personally I think a correct one. I wouldn't want my structs calling their own constructor when declared. – user42467 Dec 05 '08 at 17:00
  • But you can't write special code for m2 = new MyStruct(); can you? – R. Martinho Fernandes Feb 06 '09 at 12:14
  • 3
    onester: if you don't want the structs calling their own constructor when declared, then don't define such a default constructor! :) that's Motti's saying – Stefan Monov May 22 '10 at 21:15
  • @user42467: If the default value of every type is simply all-bits-zero, that means that the code which creates a new blank instance of a type need not know anything about it other than its size. By contrast, if the default value of a struct could be made to be anything other than all-bits-zero, then the code which creates a new blank instance of any type containing that struct would have to know about it. This could greatly complicate object construction, especially if one considers the implications for generic types. – supercat Oct 31 '12 at 19:54
  • I think you gave the most comprehensible answer in this post. I think this is the root cause why we are not allowed to use default constructor in C#. – Tarik Oct 10 '14 at 14:34
  • 8
    @Tarik. I do not agree. On the contrary, a parameterless constructor would make full sense : if I want to create a "Matrix" struct wich always have an identity matrix as a default value, how could you do it by other means ? – Elo Feb 18 '16 at 10:38
  • 1
    I'm [not sure I fully agree](https://dotnetfiddle.net/JDQ4jD) with the *"Indeed m2 could quite happily be used.."*. It may have been true in a previous C# but it's a compiler error to declare a struct, not `new` It, then try to use its members – Caius Jard Mar 10 '20 at 07:14
23

You can make a static property that initializes and returns a default "rational" number:

public static Rational One => new Rational(0, 1); 

And use it like:

var rat = Rational.One;
AustinWBryan
  • 3,249
  • 3
  • 24
  • 42
14

Shorter explanation:

In C++, struct and class were just two sides of the same coin. The only real difference is that one was public by default and the other was private.

In .NET, there is a much greater difference between a struct and a class. The main thing is that struct provides value-type semantics, while class provides reference-type semantics. When you start thinking about the implications of this change, other changes start to make more sense as well, including the constructor behavior you describe.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
  • 8
    You'll have to be a bit more explicit about how this is implied by the value vs. reference type split I don't get it... – Motti Dec 02 '08 at 19:05
  • Value types have a default value- they are not null, even if you don't define a constructor. While at first glance this doesn't preclude also defining a default constructor, the framework using this feature internal to make certain assumptions about structs. – Joel Coehoorn Dec 02 '08 at 21:50
  • @annakata: Other constructors are probably useful in some scenarios involving Reflection. Also, if generics were ever enhanced to allow a parameterized "new" constraint, it would be useful to have structs that could comply with them. – supercat Feb 24 '12 at 01:39
  • @annakata I believe it's because C# has a particular strong requirement that `new` really must be written to call a constructor. In C++ constructors are called in hidden ways, at declaration or instanciation of arrays. In C# either everything is a pointer so start at null, either it's a struct and must start at something, but when you cannot write `new`... (like array init), that would break a strong C# rule. – v.oddou Aug 25 '16 at 09:36
9

Beginning with C# 10.0, you can:

https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/struct#parameterless-constructors-and-field-initializers

asaf92
  • 1,557
  • 1
  • 19
  • 30
  • 5
    From what I see in the link you posted it will only be activated when the constructor is explicitly called and not when created via `default` or in an array... Ouch. – Motti Sep 25 '21 at 20:39
3

I haven't seen equivalent to late solution I'm going to give, so here it is.

use offsets to move values from default 0 into any value you like. here properties must be used instead of directly accessing fields. (maybe with possible c#7 feature you better define property scoped fields so they remain protected from being directly accessed in code.)

This solution works for simple structs with only value types (no ref type or nullable struct).

public struct Tempo
{
    const double DefaultBpm = 120;
    private double _bpm; // this field must not be modified other than with its property.

    public double BeatsPerMinute
    {
        get => _bpm + DefaultBpm;
        set => _bpm = value - DefaultBpm;
    }
}

This is different than this answer, this approach is not especial casing but its using offset which will work for all ranges.

example with enums as field.

public struct Difficaulty
{
    Easy,
    Medium,
    Hard
}

public struct Level
{
    const Difficaulty DefaultLevel = Difficaulty.Medium;
    private Difficaulty _level; // this field must not be modified other than with its property.

    public Difficaulty Difficaulty
    {
        get => _level + DefaultLevel;
        set => _level = value - DefaultLevel;
    }
}

As I said this trick may not work in all cases, even if struct has only value fields, only you know that if it works in your case or not. just examine. but you get the general idea.

M.kazem Akhgary
  • 18,645
  • 8
  • 57
  • 118
  • This is a good solution for the example I gave but it was really only supposed to be an example, the question is general. – Motti Sep 27 '17 at 19:13
2

Just special-case it. If you see a numerator of 0 and a denominator of 0, pretend like it has the values you really want.

Jonathan Allen
  • 68,373
  • 70
  • 259
  • 447
  • 5
    Me personally wouldn't like my classes/structs to have this kind of behaviour. Failing silently (or recovering in the way the dev guesses is best for you) is the road to uncaught mistakes. – Boris Callens Dec 03 '08 at 09:15
  • 2
    +1 This is a good answer, because for value types, you have to take into account their default value. This let's you "set" the default value with its behaviour. – IS4 Jul 16 '15 at 23:22
  • This is exactly how they implement classes such as `Nullable` (e.g. `int?`). – Jonathan Allen Jan 31 '17 at 21:01
  • That's a very bad idea. 0/0 should always be an invalid fraction (NaN). What if somebody calls ``new Rational(x,y)`` where x and y happens to be 0? – Mike Rosoft Jul 23 '19 at 11:53
  • If you have an actual constructor then you can throw an exception, preventing a real 0/0 from happening. Or if you do want it to happen, you have to add an extra bool to distinguish between default and 0/0. – Jonathan Allen Jul 29 '19 at 14:02
2

What I use is the null-coalescing operator (??) combined with a backing field like this:

public struct SomeStruct {
  private SomeRefType m_MyRefVariableBackingField;

  public SomeRefType MyRefVariable {
    get { return m_MyRefVariableBackingField ?? (m_MyRefVariableBackingField = new SomeRefType()); }
  }
}

Hope this helps ;)

Note: the null coalescing assignment is currently a feature proposal for C# 8.0.

Pang
  • 9,564
  • 146
  • 81
  • 122
Axel Samyn
  • 160
  • 1
  • 12
1

You can't define a default constructor because you are using C#.

Structs can have default constructors in .NET, though I don't know of any specific language that supports it.

Jonathan Allen
  • 68,373
  • 70
  • 259
  • 447
1

I found a simple solution for this:

struct Data
{
    public int Point { get; set; }
    public HazardMap Map { get; set; }

    public Data Initialize()
    {
        Point = 1; // Set anything you want as default
        Map = new HazardMap();
        return this;
    }
}

In code, just do:

Data input = new Data().Initialize();
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
rekaha
  • 19
  • 2
1

Since C# 10, you can define the parameterless constructor for struct.

For example:

struct S0 { }                   // ok
struct S1 { public S1() { } }   // ok
struct S2 { internal S2() { } } // error: parameterless constructor must be 'public'

For more information you can visit this link: Parameterless struct constructors

Ramil Aliyev 007
  • 4,437
  • 2
  • 31
  • 47
-1

Here's my solution to the no default constructor dilemma. I know this is a late solution, but I think it's worth noting this is a solution.

public struct Point2D {
    public static Point2D NULL = new Point2D(-1,-1);
    private int[] Data;

    public int X {
        get {
            return this.Data[ 0 ];
        }
        set {
            try {
                this.Data[ 0 ] = value;
            } catch( Exception ) {
                this.Data = new int[ 2 ];
            } finally {
                this.Data[ 0 ] = value;
            }
        }
    }

    public int Z {
        get {
            return this.Data[ 1 ];
        }
        set {
            try {
                this.Data[ 1 ] = value;
            } catch( Exception ) {
                this.Data = new int[ 2 ];
            } finally {
                this.Data[ 1 ] = value;
            }
        }
    }

    public Point2D( int x , int z ) {
        this.Data = new int[ 2 ] { x , z };
    }

    public static Point2D operator +( Point2D A , Point2D B ) {
        return new Point2D( A.X + B.X , A.Z + B.Z );
    }

    public static Point2D operator -( Point2D A , Point2D B ) {
        return new Point2D( A.X - B.X , A.Z - B.Z );
    }

    public static Point2D operator *( Point2D A , int B ) {
        return new Point2D( B * A.X , B * A.Z );
    }

    public static Point2D operator *( int A , Point2D B ) {
        return new Point2D( A * B.Z , A * B.Z );
    }

    public override string ToString() {
        return string.Format( "({0},{1})" , this.X , this.Z );
    }
}

ignoring the fact I have a static struct called null, (Note: This is for all positive quadrant only), using get;set; in C#, you can have a try/catch/finally, for dealing with the errors where a particular data type is not initialized by the default constructor Point2D(). I guess this is elusive as a solution to some people on this answer. Thats mostly why i'm adding mine. Using the getter and setter functionality in C# will allow you to bypass this default constructor non-sense and put a try catch around what you dont have initialized. For me this works fine, for someone else you might want to add some if statements. So, In the case where you would want a Numerator/Denominator setup, this code might help. I'd just like to reiterate that this solution does not look nice, probably works even worse from an efficiency standpoint, but, for someone coming from an older version of C#, using array data types gives you this functionality. If you just want something that works, try this:

public struct Rational {
    private long[] Data;

    public long Numerator {
        get {
            try {
                return this.Data[ 0 ];
            } catch( Exception ) {
                this.Data = new long[ 2 ] { 0 , 1 };
                return this.Data[ 0 ];
            }
        }
        set {
            try {
                this.Data[ 0 ] = value;
            } catch( Exception ) {
                this.Data = new long[ 2 ] { 0 , 1 };
                this.Data[ 0 ] = value;
            }
        }
    }

    public long Denominator {
        get {
            try {
                return this.Data[ 1 ];
            } catch( Exception ) {
                this.Data = new long[ 2 ] { 0 , 1 };
                return this.Data[ 1 ];
            }
        }
        set {
            try {
                this.Data[ 1 ] = value;
            } catch( Exception ) {
                this.Data = new long[ 2 ] { 0 , 1 };
                this.Data[ 1 ] = value;
            }
        }
    }

    public Rational( long num , long denom ) {
        this.Data = new long[ 2 ] { num , denom };
        /* Todo: Find GCD etc. */
    }

    public Rational( long num ) {
        this.Data = new long[ 2 ] { num , 1 };
        this.Numerator = num;
        this.Denominator = 1;
    }
}
G1xb17
  • 87
  • 7
  • 2
    This is very bad code. Why do you have an array reference in a struct? Why don't you simply have the X and Y coordinates as fields? And using exceptions for flow control is a bad idea; you should generally write your code in such a way that NullReferenceException never occurs. If you really need this - though such a construct would be better suited for a class rather than a struct - then you should use lazy initialization. (And technically, you are - completely needlessly in every but the first setting of a coordinate - setting each coordinate twice.) – Mike Rosoft Aug 05 '19 at 11:41
-2
public struct Rational 
{
    private long numerator;
    private long denominator;

    public Rational(long num = 0, long denom = 1)   // This is allowed!!!
    {
        numerator   = num;
        denominator = denom;
    }
}
eMeL
  • 327
  • 4
  • 5
  • 7
    It's allowed but it's not used when no parameters are specified https://ideone.com/xsLloQ – Motti Aug 19 '18 at 06:32