Search code examples
c#asp.net-core.net-corec#-9.0

Are C# 9 'init' properties safe to use for configuration binding (IOptions)?


I noticed that the new init properties in C# version 9 seem to bind when used in strongly typed option classes (Options Pattern).

In other words, this works:

    class SomeSettings
    {
        public string FirstSetting { get; init; }
        public bool SecondSetting { get; init; }
    }

    ...

    configuration.GetSection(nameof(SomeSettings)).Get<SomeSettings>();

But are there any potential 'gotchas' or limitations we should be aware of? (When used either with direct binding, or injected as IOptions<> with Configure().)

Admittedly, my motivation is that I'm thinking about binding and registering these instances directly at configuration time as singletons, without the pesky IOptions<> wrapper. The only reason I haven't done so far was the mutability of the properties, but init seems to solve that.


Solution

  • init lowers to a set property in IL. This was intentional so that reflection which sets properties will continue to work with init so this is safe yes.

    For more see the discussion on this starting here, and specifically this comment:

    I think this is a matter of cost to benefit.

    This is indeed a cost benefit trade off and after discussion we decided to favor init working in existing scenarios over the cost of having every single on of those scenarios change to support init properties.

    What is the most popular nuget package you can find which this will adversely affect because they'll set something they shouldn't?

    The other aspect to consider here is how far do you want to go with enforcement? The item to keep in mind here is that the runtime provides zero enforcement of readonly instance fields. That means reflection, even public reflection, can stomp all over readonly today and there is no penalty for doing so.

    One aspect we considered in LDM when discussing these type of packages is "what is the value in holding init accessors to a higher standard than readonly fields? The answer we came to is there isn't a lot of value in doing this.

    Reflection can already invalidate object immutability and this is exceedingly unlikely to change. Hence by making init accessible to reflection we're not changing the semantics here, we're just fitting with the existing system. In that sense the trade off here is very reasonable from our view point.