C# -

Attributes

Attributes in C# provide a powerful way to add metadata to your code. They enable you to annotate program elements (assemblies, types, methods, properties, etc.) with additional information that can be retrieved at runtime using reflection. This tutorial covers the basics, use cases, and advanced concepts of attributes.


1. Introduction to Attributes

Attributes are a way to attach metadata to code elements. This metadata can then be queried and used at runtime using reflection. Here's a basic example of defining and using an attribute:

        
            using System;

namespace AttributeExamples;

public class Program
{
    [Obsolete("This method is obsolete. Use NewMethod instead.")]
    public static void OldMethod()
    {
        Console.WriteLine("Old method.");
    }

    public static void NewMethod()
    {
        Console.WriteLine("New method.");
    }

    public static void Main(string[] args)
    {
        OldMethod();
        NewMethod();
    }
}
        
    

2. Creating Custom Attributes

You can create custom attributes by defining a class that derives from `System.Attribute`. Here's how to create and use a custom attribute:

        
            using System;

namespace AttributeExamples;

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class SampleAttribute : Attribute
{
    public string Description { get; }

    public SampleAttribute(string description)
    {
        Description = description;
    }
}

[Sample("This is a sample class.")]
public class SampleClass
{
    [Sample("This is a sample method.")]
    public void SampleMethod()
    {
        Console.WriteLine("Sample method called.");
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        SampleClass sample = new SampleClass();
        sample.SampleMethod();
    }
}
        
    

3. Attribute Usage

The `AttributeUsage` attribute specifies how a custom attribute can be used. This includes specifying valid targets, whether the attribute is inherited, and whether multiple instances are allowed. Here's an example:

        
            using System;

namespace AttributeExamples;

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public class SampleAttribute : Attribute
{
    public string Description { get; }

    public SampleAttribute(string description)
    {
        Description = description;
    }
}

[Sample("This is a base class.")]
public class BaseClass
{
}

public class DerivedClass : BaseClass
{
}

public class Program
{
    public static void Main(string[] args)
    {
        Type type = typeof(DerivedClass);
        object[] attributes = type.GetCustomAttributes(true);
        foreach (var attribute in attributes)
        {
            Console.WriteLine($"Attribute: {attribute}");
        }
    }
}
        
    

4. Retrieving Attributes Using Reflection

You can use reflection to retrieve attributes at runtime. Here's how to inspect and retrieve attributes applied to various code elements:

        
            using System;
using System.Reflection;

namespace AttributeExamples;

[AttributeUsage(AttributeTargets.Method)]
public class SampleAttribute : Attribute
{
    public string Description { get; }

    public SampleAttribute(string description)
    {
        Description = description;
    }
}

public class SampleClass
{
    [Sample("This is a sample method.")]
    public void SampleMethod()
    {
        Console.WriteLine("Sample method called.");
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        MethodInfo method = typeof(SampleClass).GetMethod("SampleMethod");
        var attribute = (SampleAttribute)method.GetCustomAttribute(typeof(SampleAttribute));
        Console.WriteLine($"Method: {method.Name}, Attribute Description: {attribute.Description}");
    }
}
        
    

5. Built-in Attributes

C# provides several built-in attributes, such as `Obsolete`, `Serializable`, and `Conditional`. Here's an overview of some commonly used built-in attributes and how to use them:

        
            using System;
using System.Diagnostics;

namespace AttributeExamples;

public class Program
{
    [Conditional("DEBUG")]
    public static void DebugOnlyMethod()
    {
        Console.WriteLine("Debug only method.");
    }

    [Obsolete("Use NewMethod instead.")]
    public static void OldMethod()
    {
        Console.WriteLine("Old method.");
    }

    [Serializable]
    public class SampleClass
    {
    }

    public static void Main(string[] args)
    {
        DebugOnlyMethod();
        OldMethod();
    }
}
        
    

6. Attribute Targets

Attributes can be applied to various targets, including assemblies, classes, methods, properties, and parameters. Here's an example demonstrating different attribute targets:

        
            using System;

namespace AttributeExamples;

[AttributeUsage(AttributeTargets.All)]
public class SampleAttribute : Attribute
{
    public string Description { get; }

    public SampleAttribute(string description)
    {
        Description = description;
    }
}

[Sample("Applied to class")]
public class SampleClass
{
    [Sample("Applied to method")]
    public void SampleMethod([Sample("Applied to parameter")] int parameter)
    {
    }

    [Sample("Applied to property")]
    public int SampleProperty { get; set; }
}

public class Program
{
    public static void Main(string[] args)
    {
        Type type = typeof(SampleClass);
        object[] classAttributes = type.GetCustomAttributes(false);
        Console.WriteLine($"Class Attributes: {classAttributes.Length}");
    }
}
        
    

7. Inherited Attributes

Attributes can be inherited by derived classes. You can control this behavior using the `Inherited` property of the `AttributeUsage` attribute. Here's how inherited attributes work:

        
            using System;

namespace AttributeExamples;

[AttributeUsage(AttributeTargets.Class, Inherited = true)]
public class SampleAttribute : Attribute
{
    public string Description { get; }

    public SampleAttribute(string description)
    {
        Description = description;
    }
}

[Sample("Base class attribute")]
public class BaseClass
{
}

public class DerivedClass : BaseClass
{
}

public class Program
{
    public static void Main(string[] args)
    {
        Type type = typeof(DerivedClass);
        object[] attributes = type.GetCustomAttributes(true);
        foreach (var attribute in attributes)
        {
            Console.WriteLine($"Attribute: {attribute}");
        }
    }
}
        
    

8. Conditional Attributes

The `Conditional` attribute allows you to specify that a method should be included or excluded based on a conditional compilation symbol. Here's how to use the `Conditional` attribute:

        
            using System;
using System.Diagnostics;

namespace AttributeExamples;

public class Program
{
    [Conditional("DEBUG")]
    public static void DebugOnlyMethod()
    {
        Console.WriteLine("This method is only called in debug mode.");
    }

    [Conditional("TRACE")]
    public static void TraceOnlyMethod()
    {
        Console.WriteLine("This method is only called in trace mode.");
    }

    public static void Main(string[] args)
    {
        DebugOnlyMethod();
        TraceOnlyMethod();
    }
}
        
    

9. Attribute Parameters

Attributes can have parameters, allowing you to pass information to the attribute. Here's how to define and use attribute parameters:

        
            using System;

namespace AttributeExamples;

[AttributeUsage(AttributeTargets.Method)]
public class SampleAttribute : Attribute
{
    public string Description { get; }
    public int Version { get; }

    public SampleAttribute(string description, int version)
    {
        Description = description;
        Version = version;
    }
}

public class SampleClass
{
    [Sample("This is a sample method.", 1)]
    public void SampleMethod()
    {
        Console.WriteLine("Sample method called.");
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        var method = typeof(SampleClass).GetMethod("SampleMethod");
        var attribute = (SampleAttribute)method.GetCustomAttribute(typeof(SampleAttribute));
        Console.WriteLine($"Method: {method.Name}, Description: {attribute.Description}, Version: {attribute.Version}");
    }
}
        
    

10. Using Attributes in Libraries

Attributes are commonly used in libraries to provide metadata and functionality to consumers of the library. Here's how attributes can be used in library development:

        
            using System;

namespace AttributeExamples;

[AttributeUsage(AttributeTargets.Method)]
public class LibraryAttribute : Attribute
{
    public string Information { get; }

    public LibraryAttribute(string information)
    {
        Information = information;
    }
}

public class LibraryClass
{
    [LibraryAttribute("This method does something important in the library.")]
    public void ImportantMethod()
    {
        Console.WriteLine("Important method called.");
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        var method = typeof(LibraryClass).GetMethod("ImportantMethod");
        var attribute = (LibraryAttribute)method.GetCustomAttribute(typeof(LibraryAttribute));
        Console.WriteLine($"Method: {method.Name}, Information: {attribute.Information}");
    }
}
        
    

11. Performance Considerations

While attributes provide powerful capabilities, they can have performance implications, especially when used extensively. Here are some performance considerations when using attributes:

        
            using System;
using System.Diagnostics;
using System.Reflection;

namespace AttributeExamples;

[AttributeUsage(AttributeTargets.Method)]
public class PerformanceAttribute : Attribute
{
    public string Description { get; }

    public PerformanceAttribute(string description)
    {
        Description = description;
    }
}

public class SampleClass
{
    [Performance("Performance test method.")]
    public void TestMethod()
    {
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        MethodInfo method = typeof(SampleClass).GetMethod("TestMethod");
        var sw = Stopwatch.StartNew();
        
        for (int i = 0; i < 1000000; i++)
        {
            var attributes = method.GetCustomAttributes(typeof(PerformanceAttribute), false);
        }

        sw.Stop();
        Console.WriteLine($"Reflection: {sw.ElapsedMilliseconds} ms");

        sw.Restart();

        var attr = method.GetCustomAttributes(typeof(PerformanceAttribute), false);
        for (int i = 0; i < 1000000; i++)
        {
            var attribute = (PerformanceAttribute)attr[0];
        }

        sw.Stop();
        Console.WriteLine($"Cached: {sw.ElapsedMilliseconds} ms");
    }
}
        
    

12. Conclusion

Attributes are a versatile and powerful feature in C#. By understanding and effectively using attributes, you can add rich metadata to your code and enhance its functionality. This tutorial covered the basics, creation, usage, and best practices for attributes.