0

Consider the following classes in C# 11:

class BaseClass
{
    public required string BaseProperty { get; init; }
}

class ChildClass : BaseClass
{
    public required string ChildProperty { get; init; }
}

The standard way to initialize the derived class is:

var a = new ChildClass
{
    BaseProperty = "abc",
    ChildProperty = "def"
};

This works fine but becomes tedious if the base class has a lot of properties or there are many derived classes. Is there any way to use a common, centralized initializer for the base class such that only the additional properties need to be initialized for derived classes? Something like (invented syntax follows):

void Main()
{       
    var a = new ChildClass : BaseInitializer()
    {
        ChildProperty = "def"
    };
}

BaseClass BaseInitializer()
{
    return new BaseClass
    {
        BaseProperty = "abc"
    };
}

Without this functionality it seems that the required and init keywords are only useful for basic scenarios not involving inheritance.

My question is a bit similar to Can I set SetsRequiredMembers or another attribute for only one member in C# 11?, except that question is asking how to apply a broad attribute to only one property, while I am wondering how to use two different initializers on the same instance.

Mike
  • 7,500
  • 8
  • 44
  • 62
  • 2
    Does this answer your question? [Can I set SetsRequiredMembers or another attribute for only one member in C# 11?](https://stackoverflow.com/questions/74400456/can-i-set-setsrequiredmembers-or-another-attribute-for-only-one-member-in-c-shar). See also [this proposal](https://github.com/dotnet/csharplang/discussions/6536) (and related ones). – Jeroen Mostert Nov 28 '22 at 20:08
  • This seems similar to the first point I mentioned in [my answer about the downsides of `required`](https://stackoverflow.com/a/74583049/5133585) here. It is apparently [by design](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-11.0/required-members#override-rules) that overridden properties cannot make the property less required. – Sweeper Nov 28 '22 at 20:09
  • What's the use-case here? Perhaps a tool like [Automapper](https://docs.automapper.org/en/stable/Mapping-inheritance.html) can solve the problem for you. – Xerillio Nov 28 '22 at 20:32
  • @Xerillio The use-case is as I described it in the question -- centralizing the initialization logic when a complicated base class has many properties, or is inherited by many different derived classes. In these cases we don't want to replicate a lot of common initialization code if we can avoid it. – Mike Nov 28 '22 at 22:03

1 Answers1

0

I think the way to go for this is with constructors:

var a = new ChildClass(
    BaseInitializer(), 
    childProperty: "xxx");


record BaseClass
{
    public required string BaseProperty { get; init; }
}

record ChildClass : BaseClass
{
    [SetsRequiredMembers]
    public ChildClass(BaseClass original, string childProperty) : base(original)
    {
        ChildProperty = childProperty;
    }

    public required string ChildProperty { get; init; }
}

Please note that Required Members C# 11 feature specification says:

In versions of this proposal with the init clause, we talked about being able to have the following scenario:

public class Base
{
   protected required int _field;

    protected Base() {} // Contract required that _field is set
}
public class Derived : Base
{
    public Derived() : init(_field = 1) // Contract is fulfilled and _field is removed from the required members list
    {
    }
}

However, we have removed the init clause from the proposal at this point, so we need to decide whether to allow this scenario in a limited fashion. The options we have are: (...)

tymtam
  • 31,798
  • 8
  • 86
  • 126