The original answer (see the update below):
The second one is allowed because it is not parameterless. But I wouldn't use optional parameters here because it is very confusing - if you call new Person()
, your constructor will not be executed (you can check it if you replace the default values other than null and zero):
public struct Person
{
public string Name { get; }
public int Age { get; }
public Person(string name = "Bob", int age = 42)
{
Name = name;
Age = age;
}
}
So new Person()
is the same as default(Person)
, both will use the initobj
MSIL instruction instead of calling a constructor.
So why would it be a problem if you could define a default constructor for structs? Consider the following examples.
private void Test()
{
Person p1 = new Person(); // ok, this is clear, use ctor if possible, otherwise initobj
Person p2 = default(Person); // use initobj
Person p3 = CreateGeneric<Person>(); // and here?
Person[] persons = new Person[100]; // do we have initialized persons?
}
public T CreateGeneric<T>() where T : new()
{
return new T();
}
So real parameterless constructors are not allowed for structs in C# (in CLR it is supported, though). Actually parameterless constructors were planned to be introduced in C# 6.0; however, it caused so many compatibility problems that at the end this feature was removed from the final release.
Update 2022:
Starting with C# 10 parameterless struct constructors are actually supported due to struct records with field initializers. From the docs:
If a record struct does not contain a primary constructor nor any instance constructors, and the record struct has field initializers, the compiler will synthesize a public parameterless instance constructor.
But not as if everything was obvious now. Let's revisit the examples above:
new Person()
: Even this case is a bit confusing. Obviously, if you have a parameterless constructor, it will call that one. But unlike in case of classes, if there is no parameterless constructor, it will use the initobj
instruction even if there is a constructor overload with optional parameters only (the OP's 2nd example).
default(Person)
: This is clear, the initobj
instruction will be used
CreateGeneric<Person>()
: It turns out that it also invokes the parameterless struct constructor... well, at least for the first time. But when targeting .NET Framework, it fails to invoke the constructor for subsequent calls.
new Persons[100]
: No constructor call (which is actually expected)
And the feature has some further unexpected implications:
- If a field has an initializer and there are only parameterized constructors, then
new MyStruct()
does not initialize the fields.
- Parameter default value initialization like
void Method(Person p = new Person())
also fails to call the parameterless constructor. In the meantime this was 'fixed' by emitting CS1736 if there is a parameterless constructor; otherwise, it is allowed and means just default
.
- If you target .NET Framework,
Activator.CreateInstance(Type)
also works incorrectly (behaves the same way as CreateGeneric<Person>()
above): the constructor is invoked only for the first time.
Expression.New(Type)
also works incorrectly, not just in .NET Framework but on all .NET/Core platforms prior version 6.0
And the story is not over. Now it seems that auto-synthesizing parameterless constructor will be removed, which makes the first bullet point above illegal and will be a breaking change from C# 10. But there are also further open issues such as this one, which also needs some changes to language specification.