How to Read C# Custom Attributes Using Reflection
Custom attributes in C# provide a powerful way to associate
declarative metadata with code elements such as classes, methods,
properties, and assemblies. At runtime, this metadata can be inspected
and acted upon using reflection. This article explains what custom
attributes are, how to define and apply them, and how to read their data
at runtime using the System.Reflection API with practical,
straightforward code examples.
What Are Custom Attributes?
Attributes are classes that inherit from the abstract base class
System.Attribute. When applied to a code element, an
attribute embeds additional metadata into the compiled assembly. Custom
attributes allow developers to define their own metadata tags to drive
runtime behaviors, such as serialization mapping, validation rules,
dependency injection rules, or logging configurations.
Defining a Custom Attribute
To create a custom attribute:
- Inherit from
System.Attribute. - (Optional but recommended) Decorate the class with
[AttributeUsage]to specify where the attribute can be used and whether multiple instances are permitted.
using System;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
public class DeveloperInfoAttribute : Attribute
{
public string DeveloperName { get; }
public string Version { get; }
public DeveloperInfoAttribute(string developerName, string version)
{
DeveloperName = developerName;
Version = version;
}
}Applying the Custom Attribute
You apply the attribute by enclosing its name (with or without the
Attribute suffix) in square brackets above the target
element:
[DeveloperInfo("Alice Smith", "1.0")]
public class PaymentService
{
[DeveloperInfo("Bob Jones", "1.1")]
public void ProcessTransaction()
{
// Transaction logic
}
}Reading Custom Attributes via Reflection
Reflection allows you to inspect types and their metadata at runtime.
The System.Reflection namespace provides several methods to
retrieve attribute instances from types, methods, and properties.
1. Reading an Attribute from a Class
To read an attribute applied to a class, retrieve the
Type object and call the generic extension method
GetCustomAttribute<T>():
using System;
using System.Reflection;
class Program
{
static void Main()
{
Type targetType = typeof(PaymentService);
// Retrieve the custom attribute
DeveloperInfoAttribute? classAttribute = targetType.GetCustomAttribute<DeveloperInfoAttribute>();
if (classAttribute != null)
{
Console.WriteLine($"Class: {targetType.Name}");
Console.WriteLine($"Developer: {classAttribute.DeveloperName}");
Console.WriteLine($"Version: {classAttribute.Version}");
}
}
}2. Reading an Attribute from a Method or Property
To read attributes on members (methods, properties, fields), obtain
the MemberInfo reference using reflection, then call
GetCustomAttribute<T>():
Type targetType = typeof(PaymentService);
MethodInfo? methodInfo = targetType.GetMethod("ProcessTransaction");
if (methodInfo != null)
{
DeveloperInfoAttribute? methodAttribute = methodInfo.GetCustomAttribute<DeveloperInfoAttribute>();
if (methodAttribute != null)
{
Console.WriteLine($"Method: {methodInfo.Name}");
Console.WriteLine($"Developer: {methodAttribute.DeveloperName}");
Console.WriteLine($"Version: {methodAttribute.Version}");
}
}3. Checking for Attribute Existence
If you only need to determine whether an attribute is present without
reading its properties, use Attribute.IsDefined():
Type targetType = typeof(PaymentService);
bool hasDeveloperInfo = Attribute.IsDefined(targetType, typeof(DeveloperInfoAttribute));
if (hasDeveloperInfo)
{
Console.WriteLine("The class contains developer tracking metadata.");
}4. Handling Multiple Attributes
If AllowMultiple = true was specified in the
AttributeUsage, use
GetCustomAttributes<T>() to retrieve an enumerable
collection of all applied instances:
IEnumerable<DeveloperInfoAttribute> attributes = targetType.GetCustomAttributes<DeveloperInfoAttribute>();
foreach (var attr in attributes)
{
Console.WriteLine($"{attr.DeveloperName} - {attr.Version}");
}Summary of Key Methods
GetCustomAttribute<T>(): Returns a single instance of attributeT, ornullif not found. Throws an exception if multiple instances exist.GetCustomAttributes<T>(): Returns anIEnumerable<T>containing all matching attributes applied to the element.IsDefined(): Returns a boolean indicating whether the target element has the specified attribute applied, avoiding unnecessary object instantiation.