Just a couple of months to go for .NET 7 final release, as you might
already know, some of the C# 11.0 features are already available.
In this post, let's go through a new feature that is ready to use from
Visual Studio 2022 version 17.3, and that is the feature to specify
required members in a
class,
struct
or
record (including record struct). And that's by using the brand new
modifier: required.
Let's go by an example. I will be using a class for simplicity. Consider the
following Person class.
public class Person { public string FirstName { get; set; } public string LastName { get; set; } public DateOnly? DateOfBirth { get; set; } }
Generally, it considers a bad practice if you are letting someone to create an
object without satisfying the minimum requirements of that particular object.
For example here in our scenario, we shouldn't let someone create a Person without specifying FirstName and LastName. Those are kind of mandatory for every person.
So here usually what we do is introduce a constructor and specify the
parameters that are needed to create a valid object.
public class Person { public string FirstName { get; set; } public string LastName { get; set; } public DateOnly? DateOfBirth { get; set; } public Person(string firstName, string lastName) { FirstName = firstName; LastName = lastName; } }
Now, this is looking good. But what if we remove the constructor. The caller
has no idea what basic properties are needed to be set when creating
a Person object. So we need to have some other way to declare which
properties are required.
So C# 11.0 introduced this brand new modifier: required which can be used as follows.
public class Person { public required string FirstName { get; set; } public required string LastName { get; set; } public DateOnly? DateOfBirth { get; set; } }
Now the caller can create a
Person object like below using object initialization.
Person person = new() { FirstName = "John", LastName = "Doe" };
And this is nice, isn't it? Now we don't even need to declare a constructor to
accept the required parameters. I personally prefer object initialization instead of using
a constructor, because say you have a lot of required properties, then in your
constructor, you are going to have a lengthy parameters list.
And if you attempted to create a Person without specifying the required parameters, the compiler will emit an error.
Person person = new(); // Required member 'Person.FirstName' must be set in the object initializer or attribute constructor. // Required member 'Person.LastName' must be set in the object initializer or attribute constructor.
Now say, you have some already written code where you are using a constructor
to set required properties and you have updated your required properties
with required modifier.
public class Person { public required string FirstName { get; set; } public required string LastName { get; set; } public DateOnly? DateOfBirth { get; set; } public Person(string firstName, string lastName) { FirstName = firstName; LastName = lastName; } }
And the existing callers would be creating an object like below.
Person person = new("John", "Doe"); // Above will throw a compile error
Now here you are going to get a compile error because the compiler doesn't
know that from your constructor you are setting values to required
properties. In this case, you need to attribute the constructor
with [SetsRequiredMembers] attribute like below.
[SetsRequiredMembers] public Person(string firstName, string lastName) { FirstName = firstName; LastName = lastName; }
Note: This [SetsRequiredMembers] attribute needs to be used with care.
Let's say for some reason, later you have decided
DateOfBirth is going to be a required property. Basically something like below.
Person person = new("John", "Doe"); // Person is getting created using the constructor, but required DateOfBirth isn't being set // No compile errors here because [SetsRequiredMembers] is masking the error public class Person { public required string FirstName { get; set; } public required string LastName { get; set; } public required DateOnly DateOfBirth { get; set; } [SetsRequiredMembers] public Person(string firstName, string lastName) { FirstName = firstName; LastName = lastName; } }
This code will get compiled just fine, but logically it isn't
correct. [SetsRequiredMembers] attribute is masking the expected error which is DateOfBirth isn't set. So that's something to keep in mind.
Most of the time, required properties shouldn't be allowed to be mutated later, so we
can write a more complete code something like below. Here for
the FirstName and LastName properties, I have used
init
keyword (introduced with C# 9.0) to specify that the required
parameters should get set only upon the object construction.
Person person = new() { FirstName = "John", LastName = "Doe" }; public class Person { public required string FirstName { get; init; } public required string LastName { get; init; } public DateOnly? DateOfBirth { get; set; } }
Hope this helps.
Regards,
Jaliya
Jaliya
No comments:
Post a Comment