.NET
attributes
programming
C#
software development

What are attributes in .NET?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In .NET, attributes are metadata objects attached to code elements such as classes, methods, properties, and assemblies. They let you describe behavior declaratively, so frameworks, tools, and your own runtime code can inspect that metadata without hard-coding special cases everywhere.

What an Attribute Really Is

An attribute in .NET is a class that derives from System.Attribute. When you apply it with square-bracket syntax, the compiler stores metadata in the assembly so it can be discovered later through reflection.

Here is a simple built-in example:

csharp
1using System;
2
3[Obsolete("Use NewCalculator instead.")]
4public class OldCalculator
5{
6    public int Add(int a, int b) => a + b;
7}

This does not change the method body. Instead, it adds metadata that the compiler and tools understand. In this case, callers get a warning because the type is marked as obsolete.

That is the core idea: attributes describe something about the code rather than directly implementing the behavior themselves.

Common Built-In Attributes

.NET uses attributes heavily across the platform. A few common examples:

  • '[Obsolete] marks APIs that should no longer be used.'
  • '[Serializable] marks types that can participate in serialization workflows.'
  • '[DllImport] describes interop calls into native libraries.'
  • '[Flags] tells consumers that an enum is meant to be combined bitwise.'

For example, platform invocation uses metadata from DllImport:

csharp
1using System;
2using System.Runtime.InteropServices;
3
4public static class NativeMethods
5{
6    [DllImport("kernel32.dll")]
7    public static extern uint GetTickCount();
8}
9
10Console.WriteLine(NativeMethods.GetTickCount());

The attribute tells the runtime how to locate the native function. Without that metadata, ordinary managed code would not know how to cross the boundary.

Creating a Custom Attribute

You can define your own attribute class when you want to annotate code with application-specific metadata.

csharp
1using System;
2
3[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
4public sealed class RequiresPermissionAttribute : Attribute
5{
6    public string PermissionName { get; }
7
8    public RequiresPermissionAttribute(string permissionName)
9    {
10        PermissionName = permissionName;
11    }
12}
13
14[RequiresPermission("admin")]
15public class AdminController
16{
17}

This attribute stores a permission name. By itself, it does not enforce authorization. It only provides metadata. Some other part of the system, such as middleware or a custom framework component, must read it and act on it.

Reading Attributes with Reflection

The usual way to consume custom attributes is reflection.

csharp
1using System;
2using System.Reflection;
3
4[AttributeUsage(AttributeTargets.Class)]
5public sealed class RequiresPermissionAttribute : Attribute
6{
7    public string PermissionName { get; }
8
9    public RequiresPermissionAttribute(string permissionName)
10    {
11        PermissionName = permissionName;
12    }
13}
14
15[RequiresPermission("admin")]
16public class AdminController
17{
18}
19
20var type = typeof(AdminController);
21var attribute = type.GetCustomAttribute<RequiresPermissionAttribute>();
22
23Console.WriteLine(attribute?.PermissionName);

This is where attributes become useful in real systems. Framework code can inspect types, methods, or parameters and change behavior based on the metadata it finds.

ASP.NET, test frameworks, serializers, and ORMs all rely heavily on this pattern.

Restricting Where an Attribute Can Be Used

AttributeUsage lets you define where your attribute is valid and whether it can be applied multiple times.

csharp
1[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
2public sealed class SensitiveDataAttribute : Attribute
3{
4}

This matters because it prevents accidental misuse. If an attribute makes sense only on properties, say so explicitly instead of letting it appear on arbitrary program elements.

Common Pitfalls

  • Assuming an attribute automatically changes runtime behavior. Usually, something else must read the metadata and act on it.
  • Putting too much logic inside attributes themselves. Attributes should mainly carry metadata, not become mini-frameworks.
  • Forgetting AttributeUsage, which can make a custom attribute too permissive.
  • Using reflection-heavy attribute lookups in hot paths without caching results.
  • Confusing compile-time effects, such as [Obsolete], with runtime-inspected attributes, such as custom authorization markers.

Summary

  • Attributes in .NET are metadata classes derived from System.Attribute.
  • They are applied with square-bracket syntax to assemblies, types, members, and other code elements.
  • Built-in attributes power features such as obsolescence warnings, interop, and serialization.
  • Custom attributes let you annotate code with application-specific metadata.
  • Attributes become useful when compilers, frameworks, or reflection-based code read that metadata and respond to it.

Course illustration
Course illustration

All Rights Reserved.