Skip to content
Ian Griffiths By Ian Griffiths Technical Fellow I
C# 8.0 nullable references: more type system transcendence with DisallowNull

In my last blog in this series, I talked about the AllowNull attribute. This post shows its converse, DisallowNull. As with all the attributes relating to nullable references, the goal is to improve expressiveness, enabling the compiler to report more nullable-related problems without producing spurious warnings.

DisallowNull is pretty much the mirror image of AllowNull: whereas AllowNull lets you state that a null is allowed even when the type seems to state otherwise, DisallowNull lets you state that a null is not allowed even when the type system seems to state that it would be allowed. Much like AllowNull, DisallowNull is used with asymmetric properties, and generics.

Asymmetric properties

Occasionally, you might define a property that returns null if it has not been set yet, but where if someone does set it, they must provide a non-null value. You cannot express this through the C# type system alone, because a property's get and set accessors must have the same type. If you use a nullable type, you won't get warnings when code sets the property to a null value. If you use a non-nullable type, you won't get warnings for code that uses the property without checking for null.

DisallowNull resolves this dilemma. You can define a property with a nullable type, and then apply the DisallowNull attribute. The nullability of the property's type means the compiler will flag any attempts to use the value returned by the property without first checking it for null. But the presence of this attribute means it will also report any attempts to set it to a value that might be null.

Generics

DisallowNull also gets used in generic code. It enables you to state that a particular usage of a type parameter does not accept null even when a nullable type is supplied as the type argument for that parameter. You can see this in use in the IEqualityComparer<T> interface from the .NET class library (which, as it happens, uses both AllowNull and DisallowNull):

public interface IEqualityComparer<in T>
{
    bool Equals([AllowNull] T x, [AllowNull] T y);
    int GetHashCode([DisallowNull] T obj);
}
Programming C# 10 Book, by Ian Griffiths, published by O'Reilly Media, is now available to buy.

The GetHashCode method here indicates that it does not accept nulls. So even if we instantiate this as IEqualityComparer<SomeClass?>, the effective signature of GetHashCode makes the argument type SomeClass (i.e., non-nullable) and not the SomeClass? (nullable) specified in the generic type argument. (Meanwhile, the arguments for Equals are both nullable regardless of whether the type argument is nullable or non-nullable.)

The Introduction to Rx.NET 2nd Edition (2024) Book, by Ian Griffiths & Lee Campbell, is now available to download for FREE.

This is consistent with the non-generic version of the interface:

public interface IEqualityComparer
{
    bool Equals(object? x, object? y);
    int GetHashCode(object obj);
}

Here it's clear that the Equals method accepts nulls but that GetHashCode does not. This non-generic definition expresses this more directly through the presence or absence of ?, but the generic interface needs to use the AllowNull and DisallowNull attributes.

Conclusion

DisallowNull helps us out in two scenarios that are very similar to ones where its converse, AllowNull, is useful. One is where a type is stated once, but used in multiple ways, and we want different nullability in different places. The usual example of this is a property: we only get to declare the type of a property once, but we may require different nullability for the get and set accessors. Generics may also create scenarios where a type is expressed just once (as the type parameter) but then used in different ways. And then there's a second scenario, specific to generics, in which we want to be able to say that in cases where the argument for a type parameter is a reference type, we want the non-nullable version of that type, regardless of the nullability of the type argument.

Ian Griffiths

Technical Fellow I

Ian Griffiths

Ian has worked in various aspects of computing, including computer networking, embedded real-time systems, broadcast television systems, medical imaging, and all forms of cloud computing. Ian is a Technical Fellow at endjin, and Microsoft MVP in Developer Technologies. He is the author of O'Reilly's Programming C# 10.0, and has written Pluralsight courses on WPF (and here) and the TPL. He's a maintainer of Reactive Extensions for .NET, Reaqtor, and endjin's 50+ open source projects. Technology brings him joy.