Attributes are a powerful method of associating metadata or declarative information with code.
You can think of attributes as adding tags/notes to code:
There are various uses of attributes such as
- Add meta-data to code elements.
- Configure conditional compilation.
- Decorate code as obsolete.
- Data validation rules / errors messages.
- Control serialization/deserialization.
- Azure Function bindings.
- …others
Attributes and Reflection
Attributes and Reflections are two separate, but strongly related topics.
Reflection allows you to dynamically find out at runtime what methods and properties etc. are available on your types.
Attributes, let you attach additional metadata to your code and the connection with reflection is that, you can use reflection to read that metadata.
Attributes are compiled into resulting assembly. Attributes are applied at compile-time, not at run-time.
Next, lets see some code examples. All the examples source code is available on this git repo.
Attribute Basic Example – Built-in Attributes
Lets see a basic example of built-in attributes provided by the framework itself.
You can see Key attribute is applied to Id property.
and here is the reflection code which accesses this attribute:
don’t worry if this code is not clear to you yet, we will go in to details later in the post.
Here is another built-in attribute Obsolete applied to Device class:
An Obsolete attribute is a declarative tag used while declaring a type or a member of a type to indicate that it should no longer be used.
With this attribute applied, if we now try to use this type (Device class) in our program, Visual studio will give us following warning:
So, this is another common use of attribute, mark code obsolete.
So far we’ve applied attributes to both class and its properties. Later we will see that we can apply attributes to a wide range of targets.
Attribute Example – Friendly Text for Enums
Let’s see an example use of Attributes.
Following is the updated code of Device Class. We’ve defined DeviceType enum and used it in the Device class:
and here is the console output from the program which outputs a string representation of the DeviceType enum:
Now, lets say that we want human friendly readable text instead of string representation of Enum e.g. “Virtual Device” instead of “VirtualDevice”.
Here is how we can do this via attributes:
- Create an attribute to add human friendly text to enum items.
- Use reflection to extract that text to display friendly text.
Create a custom attribute
Following code shows how to create a custom attribute for our requirement:
As you can see, that our custom-attribute is just a simple class which inherits from built-in Attribute type, with one property to hold the friendly text, which is set via constructor. This follows a naming convention that says you need to suffix your custom-attribute with Attribute.
Notice the use of AttributeUsage applied to restrict it to be applied only to fields (enum values are internally implemented as static fields). AttributeUsage is another built-in attribute provided by .NET framework itself.
Apply Custom Attribute
Following code shows that FriendlyText custom attribute is applied to enum items:
Extraction via reflection
So, we have declared and applied the custom attribute (FriendlyText). This is the first half of the solution.
The other half is to extract that custom attribute via reflection and use it in our code. The following code does this part:
and we can now use it in our code as needed:
here is the console output of the usage:
Lets see another example of custom attribute next.
Attribute Example – Attribute on a Type
For this example, we have the following setup:
Vehicles Model
here is the C# implementation of this domain model. Most of the code is self explanatory, here Vehicle class is acting as a base class and other classes (Car, Truck, FlyingCar) are derived classes with few of their own properties:
In the main method, we have following simple usage:
ReportHelper.cs
Requirement
Here, in our domain, we might have vehicles which are just for testing/prototype purposes and those should not be used in our application as actual vehicles. To facalititates this requirement, we can mark those types which can be treated as actual vehicles(e.g. can be included in reports, can be sold etc.).
We can achieve this by creating a custom attribute as follows:
Now, we can mark desired code with this attribute as shown below:
At this point, attribute is defined and applied.
Next, we need to test whether a given type has that attribute applied, and this is where reflection comes in:
we can now simply use this helper method as shown below:
This is also a very common use of attribute to mark your types for certain conditions.
Attribute Example – Attribute on a Type
Here is another example of applying attribute on a type:
Target Class to apply attribute
Custom Attribute
Following is a simple company attribute to applied to Server.
Apply Custom Attribute
Usage example
note: server.GetType and typeof(Sever) result same. typeof() is used when you know the type upfront.
Attribute Example – Attribute on a Type and Properties
Now, for this example, I created a custom attribute (InfoAttribute), which can be applied to both a Type (e.g. Server) or type’s one or more Properties. This attribute will hold some help/info text.
Following are the components:
InfoAttribute
Here is the attribute applied to type and few of its properties:
and here is the calling code and output:
You can check the source code available from this git repository for more details.
ActionFilter Attribute
I will not go into details, but ActionFilters are very common use in .NET API development.
An Action filter is an attribute that you can apply to a controller action — or an entire controller — that modifies the way in which the action is executed.
Here is link to another article which shows usage of ActionFilter Attribute on Controller actions for cross-cutting logging concern.
Summary
C# Attributes are a very powerful and useful feature. They allow us to add meta-data to our code and we can use Reflection to read that metadata. Attributes are applied at compile-time.
Attributes can be applied to a wide variety of code elements such as classes, fields, attributes, properties, methods, etc. To see a list of common targets and more information about C# attributes, you can visit the official documentation on this link.
Let me know, if you have some comments or questions. Till next time, Happy coding.
My Recent Books
Discover more from Hex Quote
Subscribe to get the latest posts sent to your email.