Daily Knowledge Drop
The AllowNull
and DisallowNull
attributes can be used to set that a property can/should allow a null
value or not.
These are used in a fairly narrow and niche use case, which won't necessarily effect the performance of code, but does eliminate compiler warnings.
Null Property
Consider a User
class with a Name
property. Our system requires that a user has a name, but does not require the user has to provide one. If none is provided, then a default is specified.
In this case, the Name will never return null, but can be set to null
.
public class User
{
private string _name;
public string Name
{
get => _name;
set => _name = value ?? "Anonymous";
}
}
If an instance of User is declared, and null
assigned to Name, the compiler will give the following warning:
Cannot convert null literal to non-nullable reference type
Changing the property type from string to string? will get rid of the warning, but this is not an entirely accurate representation of the type. Name can never have a value of and return null
- callers will never need to check Name property for null
.
A more accurate method to get rid of the warning, is to use the AllowNull
attribute (in the System.Diagnostics.CodeAnalysis namespace):
[AllowNull]
public string Name
{
get => _name;
set => _name = value ?? "Anonymous";
}
This attribute specified a pre-condition which only applies to arguments
, and since only the set
accessor makes use of an argument, the attribute only applies to this accessor, and not the get
.
Non-null Property
Consider the opposite situation now - a property which has a null
default value, but if explicitly set, is not allowed to be null
. We would want to indicate to the caller that it is possible for the value to be null
, but also that it cannot explicitly be set to null
.
Going back to the User
class, consider now we to add a Email
property. The system requires that if a user has an email address, it cannot be set to null.
The default value for an email is null
, but if it is explicitly set, it cannot be null
(for example, maybe a customer can create a user profile on an e-commerce site, without an email - but if they decide to checkout, an email is required.)
public class User
{
private string _name;
private string _email;
[AllowNull]
public string Name
{
get => _name;
set => _name = value ?? "Anonymous";
}
public string Email
{
get => _email;
set => _email = value ??
throw new ArgumentNullException(nameof(value), "Cannot set to null");
}
}
This doesn't indicate that the value could be null
. To indicate that we can make it a nullable type, string?
.
public string? Email
{
get => _email;
set => _email = value ??
throw new ArgumentNullException(nameof(value), "Cannot set to null");
}
Now there is a clear indication that the value could be null
, however it is still possible to assign a null
value to the field without any compiler time warning. Only at runtime will the exception be thrown.
The DisallowNull
attribute can be used to indicate that the parameter to the property cannot be null
:
public string? Email
{
get => _email;
set => _email = value ??
throw new ArgumentNullException(nameof(value), "Cannot set to null");
}
As with before, this attribute specified a pre-condition which only applies to arguments
, and since only the set
accessor makes use of an argument, the attribute only applies to this accessor, and not the get
.
Now if null
is assigned to the Email property, there will be a compiler warning stating:
Cannot convert null literal to non-nullable reference type
This is what we would want - an indicator that the value could be null
(the fact the type is a nullable type), but also an indicator if it is explicitly set to null
(a compiler warning)
Notes
As mentioned, while this is a fairly narrow and niche use case, it a real use case especially for library authors. While neither of the attributes are "required" to make the code function, they assist in conveying the intended usage of the properties to the user of the properties, by either showing or suppressing a compiler warning.
References
Preconditions: AllowNull and DisallowNull
Daily Drop 90: 07-06-2022
At the start of 2022 I set myself the goal of learning one new coding related piece of knowledge a day.
It could be anything - some.NET / C# functionality I wasn't aware of, a design practice, a cool new coding technique, or just something I find interesting. It could be something I knew at one point but had forgotten, or something completely new, which I may or may never actually use.
The Daily Drop is a record of these pieces of knowledge - writing about and summarizing them helps re-enforce the information for myself, as well as potentially helps others learn something new as well.On This Page