Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unintuitive behaviour with struct initialization and default arguments

public struct Test 
{
    public double Val;
    public Test(double val = double.NaN) { Val = val; }
    public bool IsValid { get { return !double.IsNaN(Val); } }
}

Test myTest = new Test();
bool valid = myTest.IsValid;

The above gives valid==true because the constructor with default arg is NOT called and the object is created with the standard default val = 0.0.
If the struct is a class the behaviour is valid==false which is what I would expect.

I find this difference in behaviour and particularly the behaviour in the struct case suprising and unintuitive - what is going on? What does the default arg on the stuct construct serve? If its useless why let this compile?

Update: To clarify the focus here is not on what the behaviour is - but rather why does this compile without warning and behave unintuitively. I.e If the default arg is not applied because in the new Test() case the constructor is not called then why let it compile?

like image 474
Ricibob Avatar asked Nov 26 '14 09:11

Ricibob


1 Answers

In C# (at least until C# 6 - see blog post), invoking new Test() is equivalent to writing default(Test) - no constructor is actually called, the default value is provided.

The default arg serves no purpose, what happens is that it is likely the result of an oversight in the implementation of the compiler, due to the fact that optional arguments were only added in C# 4:

  • The code that checks that optional arguments do not conflict with already existing overloads is unaware of a possible conflict with the initializer in the case of structs;
  • The code that translates what new Test() means is probably unaware of the existence of optional arguments;

    • After digging into comments, I noticed the following gem by Mads Torgersen:

      It is true that the compiler implementation has so far "optimized" 'new T()' to mean essentially default(T) when T is a struct. That was actually a bug - it was always supposed to call an actual parameterless constructor if there is one - which there could have been all along, since it is allowed in IL.

      For your example, it means that new Test() is effectively replaced by the compiler to default(Test) - so that is a bug, which will be fixed in the next version of Visual Studio.

In other words, you have a corner case. That would probably be a good time to look at how that behaves in the next version of Visual Studio, as that behavior is changing.

like image 98
Jean Hominal Avatar answered Nov 06 '22 22:11

Jean Hominal