Skip to main content

What is Attribute Usage?

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. The term is often used interchangeably with “attributes” or “annotations.” Attributes solve the problem of needing to attach metadata to code elements without modifying their core functionality, enabling frameworks and tools to read and interpret this information at runtime through reflection.

How it works in C#

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 is Attribute Usage important?

  1. Metadata-Driven Design (Open/Closed Principle): Attributes enable extending behavior without modifying existing code, adhering to the Open/Closed Principle by allowing new features through metadata rather than code changes.
  2. Declarative Programming (DRY Principle): They reduce boilerplate code by allowing declarative configuration that can be reused across multiple elements, eliminating repetitive imperative code.
  3. Framework Extensibility (Inversion of Control): Attributes provide hooks for frameworks to discover and configure components dynamically, enabling powerful IoC containers and plugin systems.

Advanced Nuances

Attribute Targeting and Inheritance Nuances

[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 { }

Advanced Reflection Patterns

// Using inheritance chains for attribute resolution
public static T GetAttributeInheritanceChain\<T\>(this MemberInfo member) where T : Attribute
{
    // Check the member itself
    var attr = member.GetCustomAttribute\<T\>();
    if (attr != null) return attr;
    
    // For methods, check interface implementation if applicable
    if (member is MethodInfo method && method.DeclaringType.IsInterface)
    {
        var interfaceMap = method.DeclaringType.GetInterfaceMap(method.DeclaringType);
        // Complex logic to find corresponding interface method attributes
    }
    
    return null;
}

// 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));
    }
}

How this fits the Roadmap

Attribute Usage serves as the foundation for the entire “Attributes and Reflection” section. It’s a prerequisite for understanding more advanced topics like:
  • Dynamic Type Creation: Using System.Reflection.Emit to generate types dynamically based on attribute metadata
  • Aspect-Oriented Programming: Implementing cross-cutting concerns through attributes that get woven into code
  • 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
Mastering Attribute Usage unlocks the ability to create sophisticated frameworks and libraries that can introspect and adapt to code metadata, making it essential for advanced C# development and architectural patterns.

Build docs developers (and LLMs) love