Question

Useful C# and .NET Attributes: Namespaces and Examples

csharp.net.net-attributes

Question

C# attributes provide metadata that can influence tools, frameworks, serialization, validation, and runtime behavior. For example, [Browsable(false)] can hide a property in a property browser.

Which predefined .NET attributes are commonly useful in real projects, what namespaces contain them, and when should each be used?

Short Answer

You will learn what C# attributes are, how to apply them, and which built-in .NET attributes are frequently useful for deprecation warnings, validation, serialization, nullability analysis, caller information, and designer or component metadata.

Concept

Attributes are metadata attached to a program element, such as a class, property, method, parameter, assembly, or enum value.

An attribute does not usually perform work by itself. Instead, another part of the system reads it and reacts:

  • The C# compiler reads [Obsolete] and reports a warning or error.
  • ASP.NET Core and validation libraries can read [Required] and validate incoming data.
  • JSON serializers can read [JsonPropertyName] and choose a JSON field name.
  • IDEs and static analyzers can read nullable-flow attributes such as [NotNullWhen].
  • Property-grid tools can read [Browsable(false)] and hide a property.

Attributes are classes that inherit from System.Attribute. Their names conventionally end with Attribute, but C# lets you omit that suffix when applying them. For example, [Obsolete] and [ObsoleteAttribute] mean the same thing.

Use attributes when metadata belongs naturally beside the code it describes. This keeps configuration discoverable and allows standard tools and frameworks to interpret the same declaration consistently.

Mental Model

Think of an attribute as a label attached to a box.

The box is your class, method, or property. The label does not change what is inside the box on its own. But a person or machine that sees the label knows how to handle it.

For example:

  • [Obsolete("Use SendAsync instead")] is a label telling the compiler: “warn anyone who uses this API.”
  • [JsonIgnore] is a label telling a JSON serializer: “do not put this property in the JSON output.”
  • [Required] is a label telling a validator: “this value must be supplied.”

Different readers pay attention to different labels. A JSON serializer ignores [Browsable], while a property-grid tool may use it.

Take Quiz

Syntax and Examples

Apply an attribute in square brackets immediately before the item it describes.

using System;

public class LegacyApi
{
    [Obsolete("Use CalculateTotal instead.")]
    public decimal GetTotal()
    {
        return 10m;
    }

    public decimal CalculateTotal()
    {
        return 10m;
    }
}

When code calls GetTotal(), the compiler displays the message as a warning.

Attributes may accept positional constructor arguments and named property arguments:

using System.ComponentModel;

public class ReportSettings
{
    [DisplayName("Rows per page")]
    [Description("The maximum number of report rows shown on one page.")]
    [DefaultValue(25)]
    public int PageSize { get; set; } = 25;

    [Browsable(false)]
    public string InternalCacheKey { get; set; } = "";
}

DisplayName, Description, DefaultValue, and Browsable are metadata commonly consumed by component and design-time tooling.

You can apply multiple attributes to one target:

using System.ComponentModel.DataAnnotations;
using System.Text.Json.Serialization;

public class CreateUserRequest
{
    [Required]
    [EmailAddress]
    [JsonPropertyName("email_address")]
    public string? EmailAddress { get; set; }
}

Here, validation can require a valid email address, while JSON uses email_address as the external field name.

Step by Step Execution

Consider a DTO used by a web API:

using System.ComponentModel.DataAnnotations;
using System.Text.Json.Serialization;

public class ProductRequest
{
    [Required]
    [StringLength(100, MinimumLength = 2)]
    [JsonPropertyName("product_name")]
    public string? Name { get; set; }
}

When an API receives this JSON:

{
  "product_name": "A"
}

The typical sequence is:

  1. The JSON serializer inspects ProductRequest.
  2. It finds [JsonPropertyName("product_name")] and maps the JSON field to Name.
  3. The resulting object has Name set to "A".
  4. Validation inspects [Required]; the value exists, so this rule passes.
  5. Validation inspects [StringLength(100, MinimumLength = 2)].
  6. The value has one character, so the minimum-length rule fails.
  7. An API framework can return a validation error instead of continuing with invalid input.

The attributes describe rules and mappings. The serializer and validator are the components that carry out those rules.

Real World Use Cases

Common built-in attributes and namespaces include:

AttributeNamespaceTypical use
[Obsolete]SystemWarn consumers that an API has been replaced.
[Flags]SystemMark an enum as a set of bitwise-combinable flags.
[Serializable]SystemLegacy serialization metadata; avoid using it as a default for new applications.
[Browsable], [Description], [DisplayName]System.ComponentModelControl property-grid and component metadata.
[Required], [Range], [StringLength], [EmailAddress]System.ComponentModel.DataAnnotationsExpress validation rules for models and forms.
[JsonPropertyName], [JsonIgnore], [JsonConverter]System.Text.Json.SerializationCustomize JSON contracts.
[XmlElement], [XmlIgnore], [XmlAttribute]System.Xml.SerializationCustomize XML contracts.
[DataContract], [DataMember]System.Runtime.SerializationControl opt-in data contracts where that serializer is used.
[NotNullWhen], [MaybeNull], [MemberNotNull]System.Diagnostics.CodeAnalysisHelp the compiler's nullable reference analysis.
[CallerMemberName], [CallerFilePath], [CallerLineNumber]System.Runtime.CompilerServicesAutomatically capture caller details in logging or notifications.
[Conditional]System.DiagnosticsInclude calls only when a compilation symbol is defined.

Examples in real applications:

  • An API DTO uses data annotations to reject missing or malformed input.
  • A client-facing API marks an old method [Obsolete] while users migrate to its replacement.
  • A domain model uses JSON attributes to keep C# names idiomatic while honoring an external API contract.
  • A logging helper uses caller-information attributes so callers do not manually type method names and line numbers.
  • A [Flags] enum represents permissions such as read, write, and delete.
Take Quiz

Real Codebase Usage

In production code, attributes work best when they express stable, local metadata rather than large amounts of application logic.

Deprecating an API safely

using System;

public class InvoiceClient
{
    [Obsolete("Use FetchInvoiceAsync instead.")]
    public string FetchInvoice(int id) => FetchInvoiceAsync(id).GetAwaiter().GetResult();

    public Task<string> FetchInvoiceAsync(int id) =>
        Task.FromResult($"Invoice {id}");
}

Use [Obsolete] during migrations. If continued usage must be blocked, pass true as the second argument:

[Obsolete("This method must not be used.", error: true)]
public void RemovedOperation() { }

Keeping JSON contracts explicit

using System.Text.Json.Serialization;

public sealed class CustomerResponse
{
    [JsonPropertyName("customer_id")]
    public required string CustomerId { get; init; }

    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public string? Notes { get; init; }
}

This is useful when an external service expects snake_case, but your C# code uses PascalCase.

Helping nullable analysis with a guard method

using System.Diagnostics.CodeAnalysis;

public static class Guard
{
    public static bool HasValue([NotNullWhen(true)] string? value)
    {
        return !string.IsNullOrWhiteSpace(value);
    }
}

string? name = GetName();
if (Guard.HasValue(name))
{
    Console.WriteLine(name.Length); // The compiler knows name is not null here.
}

static string? GetName() => "Ada";

This makes guard clauses clearer to both readers and the compiler.

Capturing caller details

using System.Runtime.CompilerServices;

public static class Log
{
    public static void Info(
        string message,
        [CallerMemberName] string member = "",
        [CallerLineNumber] int line = 0)
    {
        Console.WriteLine($"{member} at line {line}: {message}");
    }
}

A call such as Log.Info("Saved"); receives the calling member name and line number automatically.

Common Mistakes

Assuming every attribute changes runtime behavior

Many attributes are only useful if a compiler, framework, serializer, reflection-based tool, or analyzer reads them. This code compiles, but [Description] does not automatically display text anywhere:

using System.ComponentModel;

[Description("Represents an order.")]
public class Order { }

Use an attribute only when you know which tool or code consumes it.

Using the wrong serializer's attributes

[JsonPropertyName] belongs to System.Text.Json. It is not interpreted by every JSON library.

using System.Text.Json.Serialization;

public class User
{
    [JsonPropertyName("user_name")]
    public string Name { get; set; } = "";
}

Before adding an attribute, verify which serializer your project actually uses.

Treating validation attributes as automatic validation everywhere

[Required] declares a rule, but a program must invoke a validation system for that rule to be checked. Plain object creation does not throw an error automatically.

using System.ComponentModel.DataAnnotations;

var request = new RegistrationRequest(); // This alone does not validate it.

public class RegistrationRequest
{
    [Required]
    public string? UserName { get; set; }
}

Use framework model validation or explicitly call a validator when needed.

Confusing [Flags] with ordinary enums

Only use [Flags] when enum values are powers of two and can be combined.

using System;

[Flags]
public enum Permission
{
    None = 0,
    Read = 1,
    Write = 2,
    Delete = 4
}

Do not use sequential values such as Read = 1, Write = 2, Delete = 3; 3 overlaps with Read | Write.

Putting secrets in attributes

Attribute arguments are compiled into assembly metadata. Do not store passwords, tokens, or other secrets in them.

Overusing attributes for changing business rules

Attributes are excellent for metadata. For rules that must change frequently by customer, environment, or database configuration, use configuration or data instead of recompiling attribute values.

Comparisons

ApproachBest forKey difference
AttributeMetadata attached directly to codeA compiler, framework, or custom reader interprets it.
Configuration file or environment variableValues that vary by deploymentCan change without recompiling the application.
Fluent configurationComplex mappings and rulesKeeps metadata outside model classes and supports conditional setup.
XML documentation commentsExplanations for developersDocuments code but normally does not change behavior.
Interface or base classRequired behavior and type relationshipsChanges the type contract, not just descriptive metadata.

Attributes versus comments

A comment might say that a property is required, but tools cannot reliably act on a comment. [Required] is structured metadata that a validator can inspect.

Attributes versus fluent configuration

Attributes keep small, stable rules close to a property:

[JsonPropertyName("first_name")]
public string FirstName { get; set; } = "";

Fluent configuration is often preferable when mappings are large, when domain classes should not depend on a framework, or when configuration varies by environment.

Take Quiz

Cheat Sheet

// Basic form
[AttributeName]
[AttributeName(argument)]
[AttributeName(NamedProperty = value)]

// The Attribute suffix is optional when applying attributes.
[Obsolete]           // Same as [ObsoleteAttribute]
NeedAttributeNamespace
Deprecation warning[Obsolete("message")]System
Bitwise enum combinations[Flags]System
Hide a designer property[Browsable(false)]System.ComponentModel
Add UI metadata[DisplayName], [Description]System.ComponentModel
Validate input[Required], [Range], [StringLength]System.ComponentModel.DataAnnotations
Rename or omit JSON fields[JsonPropertyName], [JsonIgnore]System.Text.Json.Serialization
Rename or omit XML fields[XmlElement], [XmlIgnore]System.Xml.Serialization
Improve nullable analysis[NotNullWhen], [MemberNotNull]System.Diagnostics.CodeAnalysis
Capture caller location[CallerMemberName], [CallerLineNumber]System.Runtime.CompilerServices

Rules to remember:

  • Attributes are metadata; they need a consumer.
  • Attribute arguments must be compile-time-compatible values, such as strings, numbers, typeof(...), enum values, and arrays of these.
  • Check the namespace and framework/library that will read the attribute.
  • Avoid storing sensitive data in attributes.

FAQ

What are attributes in C#?

Attributes are metadata declarations placed in square brackets before code elements. Compilers, frameworks, and tools can inspect them and change warnings, validation, serialization, or other behavior.

Do I need to write Attribute at the end of an attribute name?

No. C# allows [Obsolete] instead of [ObsoleteAttribute]. The underlying class still has the full name ObsoleteAttribute.

What namespace is BrowsableAttribute in?

BrowsableAttribute is in System.ComponentModel. It is commonly used by component-model and property-grid tooling.

Does [Required] automatically validate my object?

No. It declares the validation rule. A validation framework or explicit validation call must read and enforce it.

Which attributes are used for JSON in modern .NET?

For System.Text.Json, use attributes in System.Text.Json.Serialization, including [JsonPropertyName], [JsonIgnore], and [JsonConverter].

When should I use [Obsolete]?

Use it when an API remains available temporarily but consumers should migrate to a replacement. Include a clear message that names the replacement.

Can I create my own C# attribute?

Yes. Create a class that derives from System.Attribute, then apply it where permitted. Custom attributes are useful when your application has a custom tool, framework, or reflection-based convention that will read them.

Are attributes inherited by derived classes?

It depends on the attribute's AttributeUsage declaration and the kind of target. Check the specific attribute's documentation rather than assuming inheritance.

Take Quiz

Mini Project

Description

Build a small API-style request model that uses attributes for input validation and a stable JSON contract. This mirrors a common situation where a C# application receives JSON from another application and must reject invalid values.

Goal

Create and validate a customer registration request while serializing its properties with external JSON field names.

Requirements

Use data annotation attributes to require a name and validate an email address. Use System.Text.Json attributes to expose snake_case JSON names. Validate one intentionally invalid request and print the validation errors. Serialize one valid request and print the resulting JSON.

Take Quiz

Keep learning

Related questions

View more