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?
It serves nothing. The emitted IL code wont generate a call to the constructor with the default parameter, but will call default(Test)
. It seems totally reasonable that the compiler would emit a warning saying the constructor will not be invoked (although that is an implementation detail). I file an issue on http://connect.microsoft.com
If we look at the generated IL code for:
Test myTest = new Test();
bool valid = myTest.IsValid;
We'll see:
IL_0000: ldloca.s 00 // myTest
IL_0002: initobj UserQuery.Test // default(Test);
IL_0008: ldloca.s 00 // myTest
IL_000A: call UserQuery+Test.get_IsValid
Note the call made in IL isn't a method call to the constructor (which would look like: call Test..ctor
)
, it generated a call to initobj
:
Initializes each field of the value type at a specified address to a
null reference or a 0 of the appropriate primitive type.
Unlike Newobj, initobj does not call the constructor method. Initobj
is intended for initializing value types, while newobj is used to
allocate and initialize objects.
Which means the compiler is merely disregarding the constructor with default parameters, as until C#-6.0 it is forbidden to declare such a constructor.
@JonSkeet takes this to great depth in his answer to Does using "new" on a struct allocate it on the heap or stack?
Edit:
I actually asked Mads Torgerson a question regarding the new use of the parameterless constructor in C#-6.0 which i think is related, and he said:
@Yuval and others, regarding parameterless constructors on structs:
the thing to realize is that, before and now, constructors don't
necessarily run on structs. All we did was add the ability to have an
parameterless constructor that also cannot be guaranteed to run. There
is no reasonable way to have structs that are guaranteed to have been
initialized, and parameterless constructors don't help with that.
The thing parameterless constructors help with is allowing you to have
a parameterless constructor.
I think a main source of confusion is that 'new S()' is allowed to
mean 'default(S)'. That is a historical mistake in the language and I
dearly wish I could take it away. I would strongly discourage anyone
from using 'new S()' on a struct that doesn't have a parameterless
constructor. As far as I can tell, this is in because the default(S)
syntax didn't exist in C# 1.0, so this was just the syntax used for
getting a default value of a struct.
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. We are fixing this, so that
we will call the constructor even in the generic case.
The semantics therefore is clean: new S() is the only way to run a
parameterless constructor on a struct, and it always runs that
constructor - even through generics.