Remove parameterless constructor from mapped struct/class/exception in C#
In Ice 3.7, a generated C# class mapped from a Slice struct/class/exception has a parameterless constructor, described as a "default constructor" in the Ice Manual (default constructor is the C++ terminology):
https://doc.zeroc.com/ice/3.7/language-mappings/c-sharp-mapping/client-side-slice-to-c-sharp-mapping/c-sharp-mapping-for-structures#id-.CSharpMappingforStructuresv3.7-ClassMappingforStructuresinC#
https://doc.zeroc.com/ice/3.7/language-mappings/c-sharp-mapping/client-side-slice-to-c-sharp-mapping/c-sharp-mapping-for-classes#id-.CSharpMappingforClassesv3.7-ClassConstructorsinC#
https://doc.zeroc.com/ice/3.7/language-mappings/c-sharp-mapping/client-side-slice-to-c-sharp-mapping/c-sharp-mapping-for-exceptions#id-.CSharpMappingforExceptionsv3.7-C#DefaultConstructorsforUserExceptions
As documented above, each mapped field is initialized to a specific value by this parameterless constructor, e.g. proxies and collections are initialized to null, structs-mapped-as-classes are initialized to new "empty" instances, strings are initialized to "".
What's the issue caused by this parameterless constructor?
Modern C# code uses "nullable reference types": https://learn.microsoft.com/en-us/dotnet/csharp/nullable-migration-strategies
And only a handful of field types in Slice have nullable semantics, namely proxies, classes and optional (aka tagged) types. Strings, collections, structs don't have nullable semantics.
So the most logical mapping for a Slice struct/class/exception that has a string/collection/struct field is the non-nullable mapped C# type.
Example 1:
struct Identity { string name; string category; }
// Assuming nullable reference types are enabled
public sealed partial class Identity : IEquatable<Identity>
{
public string name; // not string?
public string category; // not string?
public Identity(string name, string category) { ... } // not string?/string?
}
Example 2:
class Widget
{
Identity identity;
StringSeq nicknames;
}
// Assuming nullable reference types are enabled
public partial class Widget
{
public Identity identity; // not Identity?
public string[] nicknames; // not string[]?
public Widget(Identity identity, string[] nicknames) { ... } // not Identity? / string[]?
}
It does not make sense for a public/documented parameterless constructor to initialize non-nullable collection fields/properties to null.
The proposed solution
Don't generate a parameterless constructor. For mapped exceptions, this removes a few additional "default" constructors.
In practice, for mapped classes and exceptions, we would still generate a hidden parameterless constructor for decoding, just like slicec-cs.
A consequence of this proposal is Slice field default values would be completely ignored in C#.