[C# Tip] How to create and access custom C# Attributes by using Reflection
Just a second! π«·
If you are here, it means that you are a software developer. So, you know that storage, networking, and domain management have a cost .
If you want to support this blog, please ensure that you have disabled the adblocker for this site. I configured Google AdSense to show as few ADS as possible - I don't want to bother you with lots of ads, but I still need to add some to pay for the resources for my site.
Thank you for your understanding.
- Davide
A good, simple way to decorate and attach metadata to your classes is to create custom Attributes.
But, obviously, just creating them may not be enough: you may also want to access them at runtime.
In this article, we will see how to create custom Attributes in C# and access them at runtime.
How to create a custom C# Attribute
Attributes are nothing but classes that inherit from the System.Attribute class.
For this example, let’s create a custom Attribute called CoreAttribute that can be used to identify which version of the core product a certain class can be used for.
First, we need to define the CoreAttribute class:
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class CoreAttribute(CoreAttribute.CoreVersion version) : Attribute
{
public CoreVersion Version { get; } = version;
public enum CoreVersion { V1, V2 };
}
Let’s break down the code above:
- the class name ends with
-Attribute, which is a convention in C# for attribute classes. So,CoreAttributeis valid, whileCoreAttributeNameis not. - the class inherits from
System.Attribute. The base class does not really include anything specific to be implemented, but it is necessary to inherit from it to create a valid Attribute. - inside the definition of the class, we can have whatever we want: properties, methods, constructors, etc. In this case, I decided to add an enum named
CoreVersionand a property calledVersionto hold the version value. - in the constructor (here I’m using the Primary Constructor functionality in C# 12+), we accept a parameter of type
CoreVersionto initialise theVersionproperty.
Lastly, it’s important to add the AttributeUsage attribute to the class definition. This attribute allows us to specify how the custom attribute can be used, by using (mainly) three properties:
- an
AttributeTargetsvalue (the only one required, as it must be passed to the constructor of the attribute) is an Enum which specifies the program elements (classes, methods, properties, etc.) to which the attribute can be applied. You can combine multiple targets using a bitwise OR operation, likeAttributeTargets.Class | AttributeTargets.Parameter. In the documentation, you can find the full list of possible values. Ah, it’s a Flagged Enum, as those we described here. - an
AllowMultipleboolean, which allows or prevents users from applying multiple instances of the same attribute to a single program element. - an
Inheritedboolean, which indicates whether the attribute will be inherited by derived classes.
In this specific case, we specified that the CoreAttribute can only be applied to classes and that it cannot be applied multiple times to the same class.
How to use a custom C# Attribute
Remember that we specified that the attribute name must end with -Attribute. This is a shortcut that allows us to omit this suffix when using the attribute.
So, the CoreAttribute can be used like this:
[Core(CoreAttribute.CoreVersion.V1)]
internal class MyClassA() { }
[Core(CoreAttribute.CoreVersion.V2)]
internal class MyClassB() { }
[Core(CoreAttribute.CoreVersion.V1)]
internal class MyClassC() { }
However, you can also choose to use the full attribute class name, including the -Attribute suffix. Both [Core(...)] and [CoreAttribute(...)] are valid.
[CoreAttribute(CoreAttribute.CoreVersion.V1)]
internal class MyClassA() { }
In general, we usually prefer the first syntax, as it is more concise. But, hey, it’s up to you!
How to access a custom C# Attribute at runtime
Now, suppose we want to access the CoreAttribute applied to the classes defined above. We can do this using Reflection.
Let’s start with getting the Assembly we want to inspect. In this case, we can use the Assembly.GetExecutingAssembly() method to get the current assembly. Well, you can use GetExecutingAssembly, GetCallingAssembly, or GetEntryAssembly, depending on your needs (as we learned in this article).
Then, we can iterate through all the types in the assembly using the GetTypes() method.
private void FindAttributeUsageInAssembly(Assembly myAssembly)
{
foreach (var type in myAssembly.GetTypes())
{
bool isAttributeApplied = type.IsDefined(typeof(CoreAttribute), false);
CoreAttribute attributeInstance = type.GetCustomAttribute<CoreAttribute>();
if (attributeInstance != null)
{
output.WriteLine($"{type.Name} - {attributeInstance.Version}");
}
}
}
The magic happens in the Type class. We can use:
- the
IsDefinedmethod to check if a specific attribute is applied to the type. It accepts two parameters: the type of the attribute to check for and a boolean indicating whether to search up the inheritance chain. - the
GetCustomAttribute<T>()generic method to retrieve the instance of the specified attribute type applied to the type. If the attribute is not found, it returnsnull.
In particular, the GetCustomAttribute<T> method returns an instance of the CoreAttribute class, which we can use to access its properties, such as Version.
Further readings
I’m pretty sure you’ve already used some built-in Attributes in C#, while there are some that might not be that “famous”.
For example, have you ever used the DebuggerDisplay attribute to customise how your classes are displayed during your debugging sessions?
π Simplify debugging with DebuggerDisplay attribute dotNET | Code4IT
It’s not the first time we’ve talked about Attributes in C#. If you want to deepen your knowledge about them, I suggest you read this other introduction to Attributes, which is more focused on how to create them:
C# Tip: How to create Custom Attributes, and why they are useful | Code4IT
This article first appeared on Code4IT π§
In this article, we used Reflection to access the custom Attributes at runtime. But Reflection, although powerful, can be quite complex. If you want to deepen your knowledge about it, I suggest you read this article:
Wrapping up
This short article showed you how to create custom Attributes in C# and how to access them at runtime using Reflection.
Custom attributes can be useful in many scenarios. For example, you can add metadata to your test classes and methods to follow the Testing Vial approach for writing better and more useful tests.
I hope you enjoyed this article! Let’s keep in touch on LinkedIn, Twitter or BlueSky! π€π€
Happy coding!
π§