-
Declaring an explicit default constructor on a struct will "correctly" put field initializers into the constructor for you: public struct ThisIsFine
{
private List<string> _field = new();
public ThisIsFine() {}
public bool IsProperlyInitialized() => _field != null;
}
new ThisIsFine().IsProperlyInitialized(); // Returns true this is effectively equivalent to: public struct ThisIsFine
{
private List<string> _field;
public ThisIsFine()
{
_field = new();
}
public bool IsProperlyInitialized() => _field != null;
}
new ThisIsFine().IsProperlyInitialized(); // also Returns true Leaving out an explicit constructor will leave the code uncompilable, which is, to me, an acceptable behavior, even if not 100% convenient. Reasons for the error is, if I understand correctly, to prevent changes in behavior of existing code, when upgrading old code to C# 10, when the feature was introduced. The error is quite self-explanatory: public struct DoesNotCompile
{
private List<string> _field = new();
}
The error does however disappear, if a constructor with parameters is added: public readonly struct OhNo
{
public OhNo(int initialCapacity)
{
_field = new List<string>(initialCapacity);
}
private readonly List<string> _field = new();
public bool IsProperlyInitialized() => _field != null;
}
new OhNo().IsProperlyInitialized(); // Returns false <- this is, in my opinion, unexpected, or rather not directly obvious. This means that adding a constructor with arguments to an existing struct, while simultaneously removing a default constructor might break all usages that directly or indirectly call the default constructor: // Struct definition
public struct OhNo
{
// This constructor is added
public OhNo(int initialCapacity)
{
_field = new List<string>(initialCapacity);
}
// This constructor is removed, as the developer thinks it is "unused". The code still compiles.
// public OhNo() { }
public readonly List<string> _field = new();
}
// Existing code that suddenly changes:
var instance = new OhNo(); // This also works: The compiler generates the parameterless constructor
instance._field.Count; // Before: 0, After: Null-Ref exception Expected behavior (suggestion): public readonly struct OhNo
{
// This constructor is added
public OhNo(int initialCapacity)
{
_field = new List<string>(initialCapacity);
}
// public OhNo() { }
public readonly List<string> _field = new(); // Compiler emits a warning (or error): error CS8983: A 'struct' with field initializers must include an explicitly declared default constructor.
} Alternatively, the compiler could insert the field initializers into the parameterless constructor, as if the parameterless constructor was explicitly declared. This would, however, be a breaking change in the behavior at runtime instead of compile time. The latter is easier to work with as a developer (or could be suppressable, if it is designed as a warning). |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 2 replies
-
I have noticed that not all cases will call the explicit parameterless constructor. This is by design: ThisIsFine x = default;
// Is not the same as
ThisIsFine y = new ThisIsFine(); This complicates the topic. |
Beta Was this translation helpful? Give feedback.
-
I believe this is the relevant design notes regarding this behavior: https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-03.md#parameterless-struct-constructors-revisited |
Beta Was this translation helpful? Give feedback.
I believe this is the relevant design notes regarding this behavior: https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-03.md#parameterless-struct-constructors-revisited