Skip to main content
Attribute Usage in C# is a metadata annotation system that allows developers to declaratively add information to code elements like classes, methods, properties, and parameters. Attributes solve the problem of needing to attach metadata to code elements without modifying their core functionality.

Custom Attributes

Custom attributes are user-defined attribute classes that extend the System.Attribute base class. They allow you to create domain-specific metadata markers that can be applied to various code elements.
// Define a custom attribute with AttributeUsage to specify valid targets
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, 
                AllowMultiple = false, 
                Inherited = true)]
public class AuthorAttribute : Attribute
{
    public string Name { get; }
    public string Version { get; set; }
    
    // Constructor with required parameter
    public AuthorAttribute(string name)
    {
        Name = name;
        Version = "1.0";
    }
}

// Apply the custom attribute to a class
[Author("John Doe", Version = "2.0")]
public class DataProcessor
{
    [Author("Jane Smith")] // Applied to method
    public void ProcessData()
    {
        // Method implementation
    }
}

Reflection on Attributes

Reflection on attributes involves inspecting code elements at runtime to discover and utilize attribute information. This enables dynamic behavior based on metadata.
using System;
using System.Reflection;

public class AttributeInspector
{
    public void InspectAttributes()
    {
        Type type = typeof(DataProcessor);
        
        // Check for class-level attributes
        object[] classAttributes = type.GetCustomAttributes(true);
        foreach (object attr in classAttributes)
        {
            if (attr is AuthorAttribute authorAttr)
            {
                Console.WriteLine($"Class author: {authorAttr.Name}, Version: {authorAttr.Version}");
            }
        }
        
        // Check method-level attributes
        MethodInfo method = type.GetMethod("ProcessData");
        var methodAttributes = method.GetCustomAttributes<AuthorAttribute>();
        foreach (var attr in methodAttributes)
        {
            Console.WriteLine($"Method author: {attr.Name}");
        }
        
        // Conditional execution based on attributes
        if (method.IsDefined(typeof(AuthorAttribute), false))
        {
            Console.WriteLine("This method has author information");
        }
    }
}

// Advanced usage: Finding all types with specific attribute in assembly
public class AttributeScanner
{
    public IEnumerable<Type> FindTypesWithAttribute<TAttribute>() where TAttribute : Attribute
    {
        return Assembly.GetExecutingAssembly()
                      .GetTypes()
                      .Where(t => t.IsDefined(typeof(TAttribute)));
    }
}

Why Attribute Usage is Important

Attributes enable extending behavior without modifying existing code, adhering to the Open/Closed Principle by allowing new features through metadata rather than code changes.
They reduce boilerplate code by allowing declarative configuration that can be reused across multiple elements, eliminating repetitive imperative code.
Attributes provide hooks for frameworks to discover and configure components dynamically, enabling powerful IoC containers and plugin systems.

Advanced Nuances

Attribute Targeting and Inheritance

[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class NonInheritedAttribute : Attribute { }

[AttributeUsage(AttributeTargets.Class, Inherited = true)]
public class InheritedAttribute : Attribute { }

[Inherited]
[NonInherited]
public class BaseClass { }

public class DerivedClass : BaseClass { } 
// DerivedClass has InheritedAttribute but NOT NonInheritedAttribute

// Advanced: Conditional attribute usage based on build configuration
#if DEBUG
    [DebugOnlyAttribute]
#endif
public class DevelopmentService { }

Performance-Optimized Attribute Caching

public class AttributeCache
{
    private static readonly ConcurrentDictionary<MemberInfo, object[]> _attributeCache = new();
    
    public static T[] GetCustomAttributesCached\<T\>(MemberInfo member) where T : Attribute
    {
        return (T[])_attributeCache.GetOrAdd(member, m => m.GetCustomAttributes(typeof(T), true));
    }
}
Performance Note: Reflection on attributes can be expensive. Consider caching attribute lookups for frequently accessed metadata.

Roadmap Context

Attribute Usage serves as the foundation for the entire “Attributes and Reflection” section. It’s a prerequisite for understanding:
  • Dynamic Type Creation: Using System.Reflection.Emit to generate types dynamically based on attribute metadata
  • Aspect-Oriented Programming: Implementing cross-cutting concerns through attributes
  • Serializer/Deserializer Development: Creating custom serialization systems that use attributes for mapping rules
  • Dependency Injection Containers: Building IoC containers that discover and wire up components through attribute scanning

Build docs developers (and LLMs) love