C# Attributes: Creating and Using Custom Attributes

In C# development, you often encounter square brackets applied to classes, methods, and other code elements. These are known as Attributes, which provide a powerful mechanism for adding metadata to your code. This article explores how to create and use custom attributes in C#.

What Are Attributes?

Attributes are classes that allow you to add descriptive information, or metadata, to various code elements. According to the .NET documentation, the common language runtime enables you to add descriptive declarations called attributes that annotate elements of your code, such as types, fields, methods, and properties. These attributes are stored with the .NET Framework files' metadata and can be used to describe your code to the runtime or influence application behavior at runtime.

In .NET, attributes serve various purposes including serialization, security features, and preventing JIT compiler optimization to facilitate debugging. Let's examine some standard .NET attributes before diving into creating custom ones.

Creating Custom Attributes

Beyond the built-in .NET attributes, you can define your own custom attributes. All custom attributes must derive from the Attribute class and typically have names ending with "Attribute," though the "Attribute" suffix can be omitted when using them.

using System;

namespace MetadataAttributes
{
    [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
    public class DescriptionAttribute : System.Attribute
    {
        public string DescriptionText { get; }

        public DescriptionAttribute(string text)
        {
            DescriptionText = text;
        }
    }
}

In this example, we've created a DescriptionAttribute that inherits from the Attribute class. Its primary function is to add descriptive information to classes. Notice the [AttributeUsage] attribute applied to our class definition. This is itself an attribute that configures how our custom attribute behaves.

Understanding AttributeUsage

The AttributeUsage attribute has three important properties:

  1. validOn: Specifies the types of program elements to which the attribute can be applied. It's of type AttributeTargets, wich is an enumeration.
  2. AllowMultiple: A boolean value indicating whether multiple instances of the attribute can be applied to a single program element.
  3. Inherited: A boolean value determining whether the attribute can be inherited by derived classes.

Let's explore these properties through examples.

Exploring AllowMultiple

Consider these class definitions:

using System;
using System.Collections.Generic;

namespace MetadataAttributes
{
    [DescriptionAttribute("Human")]
    [CodeInfo("Base class")]
    public class Human
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }
}

When AllowMultiple is set to true, we can apply the DescriptionAttribute multiple times without errors. However, if AllowMultiple is false, attempting to apply the attribute multiple times will result in a compilation error.

Exploring Inheritance

Now let's look at inheritance behavior:

using System;

namespace MetadataAttributes
{
    [DescriptionAttribute("Learner")]
    public class Student : Human
    {
        public string StudentId { get; set; }
    }
}

To examine how attributes are inherited, we can use reflection:

using System;
using System.Reflection;

namespace MetadataAttributes
{
    class AttributeDemo
    {
        static void Main(string[] args)
        {
            Type studentType = typeof(Student);
            object[] attributes = studentType.GetCustomAttributes(true);
            
            foreach (var attr in attributes)
            {
                if (attr is DescriptionAttribute descAttr)
                {
                    Console.WriteLine($"{descAttr.DescriptionText} - {attr}");
                }
            }

            Console.WriteLine("-----------------------");
            
            Type humanType = typeof(Human);
            attributes = humanType.GetCustomAttributes(true);
            
            foreach (var attr in attributes)
            {
                if (attr is DescriptionAttribute descAttr)
                {
                    Console.WriteLine($"{descAttr.DescriptionText} - {attr}");
                }
            }
            
            Console.ReadLine();
        }
    }
}

The inheritance behavior depends on the combination of AllowMultiple and Inherited settings:

  • When Inherited is false, the attribute is not passed to derived classes.
  • When Inherited is true and AllowMultiple is true, derived classes receive the attribute without overriding it.
  • When Inherited is true and AllowMultiple is false, derived classes inherit the attribute but can override it.

Understanding AttributeTargets

The AttributeTargets enumeration specifies which program elements can receive an attribute. Here are the possible values:

[Flags]
public enum AttributeTargets
{
    Assembly = 1,           // Can be applied to an assembly
    Module = 2,            // Can be applied to a module
    Class = 4,             // Can be applied to a class
    Struct = 8,            // Can be applied to a struct (value type)
    Enum = 16,             // Can be applied to an enum
    Constructor = 32,      // Can be applied to a constructor
    Method = 64,           // Can be applied to a method
    Property = 128,        // Can be applied to a property
    Field = 256,           // Can be applied to a field
    Event = 512,           // Can be applied to an event
    Interface = 1024,      // Can be applied to an interface
    Parameter = 2048,      // Can be applied to a parameter
    Delegate = 4096,       // Can be applied to a delegate
    ReturnValue = 8192,    // Can be applied to a return value
    GenericParameter = 16384, // Can be applied to a generic parameter
    All = 32767            // Can be applied to any element
}

This enumeration allows you to precisely control where your custom attributes can be applied, ensuring they're used appropriately in you're codebase.

Tags: C# .NET attributes reflection Custom Attributes

Posted on Mon, 18 May 2026 18:38:16 +0000 by tzuriel