Creating and Using Custom Attributes in C#: From Basic Implementation to Reflection Mechanism

Dec 03, 2025 · Programming · 8 views · 7.8

Keywords: C# | Custom Attributes | Reflection

Abstract: This article systematically explains how to create custom attributes in C# by inheriting from the Attribute base class, using AttributeUsage to restrict application targets, and retrieving metadata at runtime through reflection. It analyzes the compile-time nature of attributes, their metadata essence, and practical application scenarios with complete code examples and best practices.

Basic Implementation of Custom Attributes

Creating custom attributes in C# requires deriving a new class from the System.Attribute base class. This class defines the metadata fields and properties carried by the attribute. For example, defining a simple custom attribute class:

public class MyCustomAttribute : Attribute
{
    public string SomeProperty { get; set; }
}

This class can now be applied as an attribute to various elements in the code. Attributes are essentially metadata that are embedded into the assembly at compile time, not created dynamically at runtime.

Attribute Application and Restrictions

Custom attributes can be applied to various program elements such as classes, methods, properties, and fields. They are applied using square bracket syntax:

[MyCustomAttribute(SomeProperty = "foo bar")]
public class Foo
{
    // Class implementation code
}

To control the scope of attribute application, the AttributeUsage attribute can be used to specify allowed target types. For example, restricting the attribute to classes only:

[AttributeUsage(AttributeTargets.Class)]
public class MyCustomAttribute : Attribute
{
    public string SomeProperty { get; set; }
}

It's important to note that attribute constructors and property setters can only accept compile-time constant values, as attribute values are determined and embedded into metadata at compile time.

Reflection Mechanism and Attribute Retrieval

Custom attributes themselves provide no functionality; they are merely attached metadata. To utilize these attributes, they must be retrieved at runtime through reflection. Here is typical code for retrieving custom attributes applied to a class:

var customAttributes = (MyCustomAttribute[])typeof(Foo).GetCustomAttributes(typeof(MyCustomAttribute), true);
if (customAttributes.Length > 0)
{
    var myAttribute = customAttributes[0];
    string value = myAttribute.SomeProperty;
    // Execute logic based on attribute value
}

Reflection allows programs to inspect their own structure, read metadata provided by attributes, and make decisions or perform operations based on this information. This is the only way custom attributes become useful.

Nature of Attributes and Design Considerations

Attributes are pure metadata that do not alter the runtime behavior of the elements they decorate. This design makes attributes ideal for annotation, configuration, and providing additional descriptive information. For example, in validation frameworks, attributes can define validation rules:

[StringLengthValidator(8, 8, MessageTemplate = "Code must have exactly 8 characters")]
public string Code { get; set; }

The validation framework then reads these attributes through reflection and executes validation logic accordingly. This pattern separates rule definition (via attributes) from rule execution (via reflection), improving code modularity and maintainability.

When developing custom attributes, several important aspects should be considered: attribute instantiation is controlled by the CLR and occurs at non-deterministic times; attributes should be designed to be lightweight, avoiding complex initialization logic; proper use of AttributeUsage can prevent attribute misuse.

Practical Application Scenarios

Custom attributes are widely used throughout the .NET ecosystem. Serialization frameworks use attributes to control serialization behavior; web frameworks use attributes to define routing and authorization rules; testing frameworks use attributes to mark test methods and test data. Understanding how attributes work helps in better utilizing these frameworks and creating custom extensions.

When designing attribute-based systems, clear documentation should be provided explaining the purpose and retrieval methods of attributes. Additionally, considering the performance overhead of reflection, attribute retrieval logic should be designed carefully to avoid frequent use in performance-critical paths.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.