In this lesson, you will learn how to use generic constraints to enforce whether a type parameter must be a non-nullable reference type or if it allows nulls.
The Problem with Generics and Nulls
When you define a generic class or method, the compiler often doesn't know if the type argument T is a value type or a reference type. Even worse, it doesn't know if T is intended to be "nullable" (like string?) or "non-nullable" (like string).
If we want to create a component that strictly handles non-nullable references—for example, a database cache that refuses to store null values—we need a way to tell the compiler to reject nullable inputs.
Using the class Constraint
To enforce that a generic type must be a non-nullable reference type, we use the class constraint combined with C#'s nullable reference type features.
When you apply the class constraint, the compiler treats T as a non-nullable reference type by default. If a user tries to pass a nullable type (like string?), the compiler will issue a warning or error, helping you maintain strict architectural boundaries.
public class DataProcessor<T> where T : class
{
// Because of 'where T : class', T is treated as non-nullable.
public void Process(T item)
{
// We are guaranteed that 'item' is not null.
Console.WriteLine(item.ToString());
}
}
Why This Matters
If you omit the class constraint, the compiler assumes T could be anything—a struct, a class, or a nullable type. By explicitly stating where T : class, you communicate your architectural intent clearly: this component is designed specifically for reference types that are expected to contain valid data.
This approach prevents "null-propagation" bugs from leaking into your collections or domain services. It moves the responsibility of null-checking from inside your methods to the type-checking phase of the compiler, making your generic architecture much more robust and predictable.
Key Takeaway: Use the where T : class constraint to guarantee that your generic code only accepts non-nullable reference types, effectively preventing nulls from entering your architectural components at compile time.