In this short article, let’s try to discuss another new feature from C# 11 – generic attributes.
What are attributes ?
If you have been using C# for very long, then you already know the answer. C# has a class System.Attribute and any class which is derived from this one basically is called as attribute. Attributes are special types of classes that can be used to decorate other types / fields. These basically provide additional functionality to the types / fields where these attributes are placed.
One very common usage of attributes are can be found in types which are used for serializations ( JsonProperty or JsonPropertyName attributes). Another place where commonly attributes can be found are for validations in ASP .NET layer, e.g. Required, Range, StringLength, etc.
How we used to pass type parameters to attributes ?
Prior to C# 11, whenever you needed to create a generic attribute implementation, the pattern to be followed was:
- Pass System.Type as parameter in the attribute constructor
- Then if needed, expose the
ParamTypeproperty which provides the System.Type information from the attribute instance, conveying which type was used to instantiate the type.
The code snippet given below shows an example. Just a simple attribute class is created and it takes System.Type as parameter. While using this attribute, we can use typeof operator, followed by the name of the type that we want to pass.
How generic attributes are going to help ?
C# 11 adds support for generic attributes. So, basically, instead of passing System.Type as parameter to constructor, we can define a generic class, like any other generic class. This class can accept one or more types as parameter.
So, with C# 11, the previous example can be rewritten as given below.
What is the advantage ?
If you are wondering what is the advantage of this approach over the previous one, I would say type safety.
Because of generic class, you can specify appropriate constraints on the type parameter to make sure that developer gets to know early (during compile time) about whether the type passed is allowed as per constraints or not.
Which types are (not) allowed ?
There are few types which are not allowed by generic attributes. The types that need metadata annotations are not allowed.
- So, dynamic and any nullable reference types are not allowed.
- Tuples, if they are created using C# tuple syntax, e.g. (int x, int y), are also not allowed.
I hope you find this information useful. Let me know your thoughts.