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:

  1. Inherit from System.Attribute.
  2. (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 attribute T, or null if not found. Throws an exception if multiple instances exist.
  • GetCustomAttributes<T>(): Returns an IEnumerable<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.