NDepend

Improve your .NET code quality with NDepend

C# 11 required members

C# 11 Required Members Title

C# 11 proposes the new keyword required that can apply to an instance property or an instance field declaration within a class, a record or a struct.

This keyword forces the caller to initialize the required field and property in the object initializer scope.

C# 11 required members

required init Property

This is quite a welcomed addition to improve object initialization scenario, especially concerning properties with an init setter that can only be initialized in the constructor and in the object initializer scope.

C# 11 required init property

Limitations of Required Members

The keyword required comes with a few limitations illustrated by the screenshot below:

  • required cannot apply to a private member since the keyword’s restriction acts outside of the parent class, when a caller instantiates it. Notice that a field like _RequiredField in the first code sample should not be mutable publicly, as explained into this NDepend rule Fields should be declared as private. Thus required field is a code smell.
  • required cannot apply to a static member since it is designed to be used only at object instantiation time.
  • required cannot apply to a read-only member that can only be assigned from within a constructor.

C# 11 required members limitations

Why required members?

Object initializer scope was introduced to overcome some constructor well-known limitations:

  • Positional: With constructor, position of parameters must be respected by the caller.
  • Lack of flexibility: if the client only wants a few states to be initialized (quite a common need in tests) he/she has to provide default values for other constructor parameters…
  • Overloading: …unless the constructor provides many overloads or default parameters which leads to confusion.
  • Breaking change: When introducing a new property, either all constructors must provide a default parameter or clients get broken.
  • Redundancy: in a hierarchy of classes, when one add a new property to the base class, its constructor(s) has a new parameter that must be added in the constructors of all derived classes.

Until now it was not possible to require object initializer scopes to set a member which led to favor constructor in such scenario.

Required Member and Constructor

The keyword required is actually part of the public contract of all constructor(s) of the class. The compiler doesn’t attempt to detect if a constructor actually assigns it:

C# 11 required and constructor

However the new .NET 7.0 attribute System.Diagnostics.CodeAnalysis.SetsRequiredMembersAttribute can tag a constructor to relieve this contract on the tagged constructor only. Thus this code below compiles properly:

If in the code above we comment this.Prop = "hello"; a CS8618 warning on the Foo() constructor explains that the non-nullable property Prop must contain a non-null value when exiting the constructor.

required and Property Overriding

There are a few limitations when it comes to required and property overriding:

  • The keyword cannot be used in the context of an interface. As we saw the keyword required influences the public contract of constructor(s) and an interface doesn’t have any constructor.
  • required cannot be used in the context of explicit implementation overriding.
  • When overriding a required virtual or abstract property, the keyword required must be precised again.

C# 11 required and abstraction

required and record C# 9.0 positional property syntax

Since there is no parameterless constructor generated by the C# 9.0 positional property syntax, there is no need for the required keyword in this situation.

C# 11 required and C# 9.0 positional property syntax

required decompiled

The keyword required leads the C# compiler to tag the parent class and required properties and fields with the new .NET 7.0 attribute System.Runtime.CompilerServices.RequiredMemberAttribute.

Conclusion

With constructors and object initializer scopes, C# has two syntaxes to deal with object instantiation. We saw that the new required keyword is a welcomed addition for object initializer situations. However this new keyword is not well suited when there is a mix of constructors overloading and init properties because of the need to tag constructors with [SetsRequiredMembers] which is not smooth syntax.

My dad being an early programmer in the 70's, I have been fortunate to switch from playing with Lego, to program my own micro-games, when I was still a kid. Since then I never stop programming.

I graduated in Mathematics and Software engineering. After a decade of C++ programming and consultancy, I got interested in the brand new .NET platform in 2002. I had the chance to write the best-seller book (in French) on .NET and C#, published by O'Reilly and also did manage some academic and professional courses on the platform and C#.

Over my consulting years I built an expertise about the architecture, the evolution and the maintenance challenges of large & complex real-world applications. It seemed like the spaghetti & entangled monolithic legacy concerned every sufficiently large team. As a consequence, I got interested in static code analysis and started the project NDepend in 2004.

Nowadays NDepend is a full-fledged Independent Software Vendor (ISV). With more than 12.000 client companies, including many of the Fortune 500 ones, NDepend offers deeper insight and full control on their application to a wide range of professional users around the world.

I live with my wife and our twin kids Léna and Paul in the beautiful island of Mauritius in the Indian Ocean.

Comments:

  1. I find a lot wrong with your argument on the problems that required fixes.

    “Positional: With constructor, position of parameters must be respected by the caller.”

    This is not true, you can always choose to name your arguments so your constructor will look almost identical to your property initialization.

    “Lack of flexibility: if the client only wants a few states to be initialized (quite a common need in tests) he/she has to provide default values for other constructor parameters…”

    You shouldn’t be listing out every possible argument in the constructor. Simply reserve that for ones that are required and let users set the properties, by other means, on those they wish to set.

    “Overloading: …unless the constructor provides many overloads or default parameters which leads to confusion.

    “Breaking change: When introducing a new property, either all constructors must provide a default parameter or clients get broken.”

    Once again, only add arguments to the constructor that you need to take. Also, you’d break all clients by adding a required property just as you would by adding a new argument to the constructor.

    At the end of the day, required is a way to try and replace constructors…something that I don’t understand. There is nothing wrong with constructors and this syntax really just goes to prove it. It’s yet another junk feature added in that only furthers bloating the language.

Leave a Reply

Your email address will not be published. Required fields are marked *