Creating Custom Attributes in C#

In C#, attributes are a powerful way to add metadata to your code.
You’ve probably seen attributes like [Obsolete], [Serializable], or [Required] before.
But did you know you can also create your own custom attributes?

In this article, we’ll walk through the basics of creating and using custom attributes in C# — step by step.

What is an Attribute?

An attribute in C# is a class that inherits from System.Attribute.
You can use attributes to decorate classes, methods, properties, parameters, and more — to store additional information about those elements.
Attributes are metadata compiled into your program. Attributes themselves do not add any functionality to a class, property or module – just data. However, using reflection, one can leverage those attributes in order to create functionality.

For example:

[Obsolete("This method is deprecated. Use NewMethod instead.")]
public void OldMethod() { }

public class Person
{
[Sanitize]
public string Name {get; set;}
}

Here, the Obsolete attribute tells the compiler to warn you when OldMethod is used.
The Sanitize attribute, for example, indicates that a field of a DTO should be cleaned of special characters.

Why Create Custom Attributes?

Built-in attributes are useful, but sometimes you need to attach your own metadata.
For example:

  • Mark certain classes as “special” in your application.
  • Add version or author information.
  • Configure behavior at runtime.

How to Create a Custom Attribute

Let’s create a simple custom attribute that marks a property as required — something similar to data annotations.

Step 1: Define the Attribute Class

We want our attribute to work on properties, so we specify AttributeTargets.Property.

[AttributeUsage(AttributeTargets.Property)]
public class RequiredPropertyAttribute : Attribute
{
    public string ErrorMessage { get; set; }

    public RequiredPropertyAttribute(string errorMessage = "This property is required.")
    {
        ErrorMessage = errorMessage;
    }
}

Explanation:

  • The class inherits from System.Attribute.
  • We use [AttributeUsage(AttributeTargets.Property)] to ensure it can only be applied to properties.
  • The constructor has an optional ErrorMessage parameter.

Step 2: Use the Custom Attribute

Now we can decorate properties in our class with the RequiredPropertyAttribute:

public class Person
{
    [RequiredProperty("Name cannot be empty.")]
    public string Name { get; set; }

    public int Age { get; set; }

    [RequiredProperty]
    public string Email { get; set; }
}

Here:

  • Name and Email are marked as required.
  • Age is optional.

Step 3: Validate Properties Using Reflection

At runtime, you can write a small utility that checks if all required properties have values:

using System;
using System.Reflection;

public class Program
{
    public static void Main()
    {
        var person = new Person
        {
            Name = null,
            Age = 30,
            Email = null
        };

        ValidateRequiredProperties(person);
    }

    public static void ValidateRequiredProperties(object obj)
    {
        var type = obj.GetType();
        foreach (var prop in type.GetProperties())
        {
            var attr = prop.GetCustomAttribute<RequiredPropertyAttribute>();
            if (attr != null)
            {
                var value = prop.GetValue(obj);
                if (value == null || (value is string s && string.IsNullOrWhiteSpace(s)))
                {
                    Console.WriteLine($"Property '{prop.Name}' is required. {attr.ErrorMessage}");
                }
            }
        }
    }
}

Output:

Property 'Name' is required. Name cannot be empty.
Property 'Email' is required. This property is required.

Summary

✅ We defined a custom attribute that applies only to properties.
✅ We used it to mark certain properties as required.
✅ We wrote a simple validator to check for missing required properties at runtime.

This pattern is very useful for scenarios like:

  • Input validation.
  • Mapping metadata for ORM frameworks.
  • Generating UI dynamically based on attributes.
  • Aspect Oriented Programming

Lascia un commento