C#のアノテーションとは?Attribute(属性)の使い方を初心者向けにわかりやすく解説
はじめに
C#について調べていると、「アノテーション」という言葉を見かけることがあります。しかし、C#ではJavaのように「アノテーション」という名前の機能が正式に用意されているわけではありません。
C#でアノテーションに近い役割を持つ機能は、**Attribute(属性)**です。
Attributeを使うと、クラス、メソッド、プロパティなどに追加情報を付けられます。たとえば「この項目は必須入力です」「このメソッドは古いので使わないでください」「このクラスは特別な処理の対象です」といった情報を、コード上に宣言できます。
この記事では、C#のアノテーションとして扱われるAttributeについて、初心者向けに基本から使い方、カスタムAttribute、リフレクションでの取得方法までわかりやすく解説します。
1. C#のアノテーションとは?Attribute(属性)の基本
1-1. C#における「アノテーション」はAttribute(属性)を指すことが多い
C#で「アノテーション」と言われる場合、多くは**Attribute(属性)**のことを指します。
Javaでは@Overrideや@Deprecatedのように、@を使ってアノテーションを記述します。一方、C#では角括弧[]を使ってAttributeを記述します。
C#[Obsolete]
public void OldMethod()
{
Console.WriteLine("古いメソッドです");
}
この[Obsolete]がC#のAttributeです。
Obsoleteは「このメソッドは古くなっている」という情報をコンパイラに伝えるための属性です。これにより、そのメソッドを使用したときに警告を出せます。
1-2. Attributeはクラス・メソッド・プロパティに付ける追加情報
Attributeは、さまざまなコード要素に付けられます。
たとえば、次のような対象にAttributeを指定できます。
C#[Serializable]
public class User
{
[Required]
public string Name { get; set; }
[Obsolete]
public void OldLogin()
{
}
}
この例では、クラス、プロパティ、メソッドにそれぞれAttributeが付いています。
[Serializable]はクラスに対する追加情報、[Required]はプロパティに対する追加情報、[Obsolete]はメソッドに対する追加情報です。
このようにAttributeは、コードに対して意味や設定を追加するために使われます。
1-3. Attributeは処理そのものではなくメタデータとして扱われる
Attributeを理解するうえで重要なのは、Attributeを付けただけでは処理が自動的に実行されるわけではないという点です。
Attributeは、基本的にはメタデータです。メタデータとは、コードそのものに付ける追加情報のことです。
たとえば、次のように[Required]を付けたとします。
C#public class User
{
[Required]
public string Name { get; set; }
}
このAttributeは「Nameは必須です」という情報を表しています。しかし、[Required]を付けただけで自動的に入力チェックが実行されるわけではありません。
ASP.NET Core MVCやバリデーション処理など、Attributeを読み取る仕組みがある場合に、その情報が利用されます。
1-4. Javaのアノテーションとの違いを初心者向けに解説
JavaのアノテーションとC#のAttributeは、考え方としてはよく似ています。どちらも、クラスやメソッドなどに追加情報を付けるための仕組みです。
ただし、書き方が異なります。
Javaでは次のように書きます。
Java@Override
public String toString() {
return "User";
}
C#では次のように書きます。
C#[Obsolete]
public void OldMethod()
{
}
Javaは@アノテーション名、C#は[Attribute名]という形式です。
また、C#ではAttributeクラスを継承して独自のAttributeを作成できます。これはJavaのカスタムアノテーションに近い使い方です。
2. C#のAttribute(属性)でできること
2-1. コンパイラやツールに追加情報を伝える
Attributeは、コンパイラや開発ツールに追加情報を伝えるために使われます。
代表的な例がObsoleteです。
C#[Obsolete("このメソッドは古いため、NewMethodを使用してください")]
public void OldMethod()
{
}
このように書くと、OldMethodを呼び出したときに警告が表示されます。
C#OldMethod(); // 警告が表示される
これにより、古いコードの使用を避けるように開発者へ知らせることができます。
2-2. 実行時にリフレクションで属性情報を取得する
Attributeは、実行時にリフレクションを使って取得できます。
リフレクションとは、プログラムの実行中にクラスやメソッド、プロパティなどの情報を調べる仕組みです。
たとえば、あるクラスに特定のAttributeが付いているかを確認し、その情報に応じて処理を変えることができます。
C#var attribute = typeof(User).GetCustomAttributes(false);
このようにして、コードに付けられたAttributeを実行時に読み取れます。
2-3. バリデーションや表示名の指定に利用する
C#のAttributeは、入力チェックや画面表示にもよく使われます。
たとえば、次のようなコードです。
C#public class User
{
[Required]
[StringLength(20)]
[Display(Name = "ユーザー名")]
public string Name { get; set; }
}
この例では、Nameプロパティに対して次の情報を指定しています。
[Required]は必須入力、[StringLength(20)]は最大20文字、[Display(Name = "ユーザー名")]は表示名を表します。
ASP.NET Core MVCなどでは、これらのAttributeを利用してフォームの入力チェックやエラーメッセージの表示ができます。
2-4. フレームワークの設定をコード上で宣言できる
Attributeは、フレームワークの設定をコード上で宣言するためにも使われます。
たとえば、ASP.NET Core MVCではルーティングにAttributeを使います。
C#[Route("users")]
public class UserController : Controller
{
[HttpGet("{id}")]
public IActionResult Details(int id)
{
return View();
}
}
この例では、[Route]や[HttpGet]によって、URLとアクションメソッドの対応をコード上に宣言しています。
設定ファイルではなく、コードに直接設定を書けるため、処理の意図がわかりやすくなる場合があります。
3. C#のAttributeの基本的な書き方
3-1. Attributeは角括弧[]で指定する
C#のAttributeは、角括弧[]を使って指定します。
C#[AttributeName]
public class Sample
{
}
実際には、ObsoleteやRequiredなどのAttribute名を指定します。
C#[Obsolete]
public void OldMethod()
{
}
Attributeは、対象となるクラスやメソッド、プロパティの直前に書きます。
3-2. クラスにAttributeを付ける書き方
クラスにAttributeを付ける場合は、クラス定義の直前に記述します。
C#[Serializable]
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
}
この例では、Productクラスに[Serializable]を付けています。
Serializableは、そのクラスがシリアライズ可能であることを示すAttributeです。
3-3. メソッドにAttributeを付ける書き方
メソッドにAttributeを付ける場合は、メソッド定義の直前に記述します。
C#public class Sample
{
[Obsolete("Use NewMethod instead.")]
public void OldMethod()
{
Console.WriteLine("Old");
}
public void NewMethod()
{
Console.WriteLine("New");
}
}
この例では、OldMethodに[Obsolete]を付けています。
これにより、OldMethodを使ったときに警告を表示できます。
3-4. プロパティにAttributeを付ける書き方
プロパティにAttributeを付ける場合も、プロパティの直前に記述します。
C#public class User
{
[Required]
public string Name { get; set; }
[StringLength(100)]
public string Email { get; set; }
}
この例では、Nameに[Required]、Emailに[StringLength]を付けています。
フォーム入力やデータ検証でよく使われる書き方です。
3-5. Attributeに引数を渡す書き方
Attributeには、引数を渡せるものがあります。
C#[Obsolete("このメソッドは古いため使用しないでください")]
public void OldMethod()
{
}
また、名前付き引数のようにプロパティを指定することもできます。
C#[Display(Name = "メールアドレス")]
public string Email { get; set; }
このように、Attributeに値を渡すことで、より具体的な情報を設定できます。
4. よく使うC#標準Attributeの例
4-1. Obsolete:古いコードであることを警告する
Obsoleteは、古くなったクラスやメソッド、プロパティに付けるAttributeです。
C#[Obsolete("このメソッドは廃止予定です。NewMethodを使用してください。")]
public void OldMethod()
{
}
このメソッドを呼び出すと、コンパイル時に警告が表示されます。
さらに、第2引数にtrueを指定すると、警告ではなくエラーにできます。
C#[Obsolete("使用禁止です。", true)]
public void OldMethod()
{
}
古いAPIを段階的に廃止したい場合に便利です。
4-2. Required:必須入力を表す
Requiredは、値が必須であることを表すAttributeです。
C#using System.ComponentModel.DataAnnotations;
public class User
{
[Required]
public string Name { get; set; }
}
主にASP.NET Core MVCやデータ検証で使われます。
ただし、[Required]を付けただけでは自動的にチェックされません。バリデーションを実行する仕組みが必要です。
4-3. StringLength・MaxLength:文字数制限を指定する
StringLengthやMaxLengthは、文字列の長さを制限するために使います。
C#public class User
{
[StringLength(20)]
public string Name { get; set; }
[MaxLength(100)]
public string Email { get; set; }
}
StringLengthは主に文字列の最大長や最小長を指定できます。
C#[StringLength(20, MinimumLength = 3)]
public string Name { get; set; }
MaxLengthは最大長を指定するために使われます。
どちらも入力チェックやデータベース設計と組み合わせて使われることが多いAttributeです。
4-4. DisplayName・Display:表示名を指定する
DisplayNameやDisplayは、プロパティの表示名を指定するために使います。
C#using System.ComponentModel;
public class User
{
[DisplayName("ユーザー名")]
public string Name { get; set; }
}
Displayを使う場合は、次のように書きます。
C#using System.ComponentModel.DataAnnotations;
public class User
{
[Display(Name = "メールアドレス")]
public string Email { get; set; }
}
画面上に表示する項目名や、エラーメッセージの表示に利用されます。
4-5. Serializable:シリアライズ対象であることを示す
Serializableは、クラスがシリアライズ可能であることを示すAttributeです。
C#[Serializable]
public class User
{
public string Name { get; set; }
}
シリアライズとは、オブジェクトを保存や通信に適した形式へ変換することです。
現在のC#開発では、JSONシリアライズなどを使う場面が多く、必ずしも[Serializable]を使うとは限りませんが、Attributeの代表例としてよく登場します。
5. カスタムAttributeの作り方
5-1. Attributeクラスを継承して独自属性を定義する
C#では、独自のAttributeを作成できます。
カスタムAttributeを作るには、System.Attributeクラスを継承します。
C#public class DeveloperAttribute : Attribute
{
public string Name { get; }
public DeveloperAttribute(string name)
{
Name = name;
}
}
この例では、開発者名を表すDeveloperAttributeを定義しています。
5-2. AttributeUsageで属性を付けられる対象を制限する
AttributeUsageを使うと、そのAttributeをどこに付けられるかを制限できます。
C#[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class DeveloperAttribute : Attribute
{
public string Name { get; }
public DeveloperAttribute(string name)
{
Name = name;
}
}
この例では、DeveloperAttributeをクラスとメソッドにだけ付けられるようにしています。
よく使う対象には、次のようなものがあります。
C#AttributeTargets.Class
AttributeTargets.Method
AttributeTargets.Property
AttributeTargets.Field
対象を制限しておくと、誤った場所にAttributeを付けることを防げます。
5-3. コンストラクタで必須の値を受け取る
Attributeで必須の値を受け取りたい場合は、コンストラクタを使います。
C#public class RoleAttribute : Attribute
{
public string RoleName { get; }
public RoleAttribute(string roleName)
{
RoleName = roleName;
}
}
この場合、Attributeを使うときには必ず値を指定する必要があります。
C#[Role("Admin")]
public class AdminService
{
}
"Admin"は必須の値としてコンストラクタに渡されます。
5-4. プロパティで任意の値を設定する
必須ではない値を設定したい場合は、プロパティを使います。
C#public class RoleAttribute : Attribute
{
public string RoleName { get; }
public string Description { get; set; }
public RoleAttribute(string roleName)
{
RoleName = roleName;
}
}
使う側では、次のように名前付きで指定できます。
C#[Role("Admin", Description = "管理者用のサービスです")]
public class AdminService
{
}
このように、コンストラクタ引数は必須値、プロパティは任意値として使うとわかりやすくなります。
5-5. カスタムAttributeをクラスやメソッドに付ける
作成したカスタムAttributeは、標準Attributeと同じように使えます。
C#[Developer("Yamada")]
public class OrderService
{
[Developer("Suzuki")]
public void CreateOrder()
{
}
}
ここでは、クラスとメソッドにDeveloperAttributeを付けています。
なお、Attribute名の末尾にあるAttributeは省略できます。
つまり、DeveloperAttributeというクラス名でも、使うときは[Developer]と書けます。
6. リフレクションでAttributeを取得する方法
6-1. GetCustomAttributeで属性を取得する
Attributeは、リフレクションを使って取得できます。
まず、次のようなカスタムAttributeを用意します。
C#[AttributeUsage(AttributeTargets.Class)]
public class DeveloperAttribute : Attribute
{
public string Name { get; }
public DeveloperAttribute(string name)
{
Name = name;
}
}
クラスにAttributeを付けます。
C#[Developer("Yamada")]
public class OrderService
{
}
取得するには、GetCustomAttributeを使います。
C#using System.Reflection;
var attribute = typeof(OrderService)
.GetCustomAttribute<DeveloperAttribute>();
if (attribute != null)
{
Console.WriteLine(attribute.Name);
}
実行すると、Yamadaが表示されます。
6-2. GetCustomAttributesで複数の属性を取得する
複数のAttributeを取得したい場合は、GetCustomAttributesを使います。
C#var attributes = typeof(OrderService).GetCustomAttributes();
foreach (var attribute in attributes)
{
Console.WriteLine(attribute.GetType().Name);
}
特定の型のAttributeだけを複数取得することもできます。
C#var developers = typeof(OrderService)
.GetCustomAttributes<DeveloperAttribute>();
foreach (var developer in developers)
{
Console.WriteLine(developer.Name);
}
複数のAttributeを扱う場合に便利です。
6-3. IsDefinedで属性が付いているか判定する
Attributeが付いているかどうかだけを確認したい場合は、IsDefinedを使えます。
C#bool exists = typeof(OrderService)
.IsDefined(typeof(DeveloperAttribute), false);
if (exists)
{
Console.WriteLine("DeveloperAttributeが付いています");
}
Attributeの値までは必要なく、「付いているかどうか」だけ知りたい場合に使いやすい方法です。
6-4. 取得したAttributeの値を使って処理を分岐する
Attributeの値を取得すれば、その値に応じて処理を分けられます。
C#var attribute = typeof(AdminService)
.GetCustomAttribute<RoleAttribute>();
if (attribute?.RoleName == "Admin")
{
Console.WriteLine("管理者向けの処理を実行します");
}
else
{
Console.WriteLine("通常処理を実行します");
}
このように、Attributeを目印として使い、実行時の処理を切り替えることができます。
ただし、処理そのものをAttributeの中に大量に書くのではなく、Attributeはあくまで判断材料として使うのが基本です。
6-5. リフレクション利用時の注意点
リフレクションは便利ですが、通常のプロパティアクセスやメソッド呼び出しに比べると負荷が高くなりやすいです。
そのため、毎回大量にAttributeを取得するような処理は注意が必要です。
たとえば、次のような工夫が考えられます。
C#private static readonly DeveloperAttribute? CachedAttribute =
typeof(OrderService).GetCustomAttribute<DeveloperAttribute>();
頻繁に使うAttribute情報はキャッシュすることで、パフォーマンスへの影響を抑えられます。
また、リフレクションを使いすぎるとコードの流れが追いにくくなるため、必要な場面に絞って使うことが大切です。
7. C#のAttributeの実践的な使いどころ
7-1. 入力チェック・バリデーションで使う
Attributeの代表的な使いどころは、入力チェックです。
C#public class RegisterUser
{
[Required]
[StringLength(20)]
public string UserName { get; set; }
[Required]
[EmailAddress]
public string Email { get; set; }
}
このように書くことで、どのプロパティにどのような入力ルールがあるのかをコード上でわかりやすく表現できます。
ASP.NET Core MVCやBlazorなどでは、これらのAttributeを利用して入力検証を行えます。
7-2. APIやMVCのルーティングで使う
Webアプリケーションでは、APIやMVCのルーティングにAttributeがよく使われます。
C#[ApiController]
[Route("api/users")]
public class UsersController : ControllerBase
{
[HttpGet("{id}")]
public IActionResult GetUser(int id)
{
return Ok();
}
}
この例では、[Route]や[HttpGet]によってURLのルールを指定しています。
コードを見ただけで、どのURLに対応する処理なのかを把握しやすくなります。
7-3. テストコードで使う
テストコードでもAttributeはよく使われます。
たとえば、xUnitでは次のように[Fact]を使います。
C#public class CalculatorTests
{
[Fact]
public void Add_ReturnsCorrectResult()
{
var result = 1 + 2;
Assert.Equal(3, result);
}
}
[Fact]は、このメソッドがテストメソッドであることを表します。
テストフレームワークは、このAttributeを読み取ってテスト対象のメソッドを見つけます。
7-4. ログ出力や権限チェックの目印として使う
カスタムAttributeを使えば、ログ出力や権限チェックの目印としても利用できます。
C#[RequirePermission("Admin")]
public void DeleteUser()
{
}
このようなAttributeを付けておき、実行前にリフレクションやフレームワークの仕組みで確認すれば、「管理者だけ実行できる処理」として扱えます。
ただし、セキュリティに関わる処理では、Attributeを付けるだけで安心せず、実際にチェック処理が正しく実行される設計にする必要があります。
7-5. 独自フレームワークや共通処理で使う
社内用の共通処理や独自フレームワークでもAttributeは役立ちます。
たとえば、次のような使い方が考えられます。
C#[CsvColumn("ユーザー名")]
public string Name { get; set; }
このAttributeを読み取って、CSVのヘッダー名とプロパティを対応付けることができます。
ほかにも、ログ対象の指定、画面表示項目の制御、権限レベルの指定、処理対象クラスのマーキングなどに利用できます。
8. C#のAttributeを使うときの注意点
8-1. Attributeを付けただけでは処理は実行されない
初心者が特に間違えやすいのが、「Attributeを付ければ自動的に処理が動く」と考えてしまうことです。
たとえば、次のように書いても、これだけで入力チェックが実行されるわけではありません。
C#[Required]
public string Name { get; set; }
Requiredの情報を読み取り、検証する仕組みが必要です。
ASP.NET Core MVCなどのフレームワークでは、その仕組みが用意されているため自動的に動いているように見えます。
しかし実際には、フレームワークがAttributeを読み取って処理しています。
8-2. Attribute名の末尾Attributeは省略できる
カスタムAttributeを作るとき、クラス名は通常〇〇Attributeという名前にします。
C#public class SampleAttribute : Attribute
{
}
しかし、使うときは末尾のAttributeを省略できます。
C#[Sample]
public class TestClass
{
}
もちろん、省略せずに書くこともできます。
C#[SampleAttribute]
public class TestClass
{
}
一般的には、省略して書くことが多いです。
8-3. 使いすぎるとコードの意図が分かりにくくなる
Attributeは便利ですが、使いすぎるとコードが読みにくくなることがあります。
C#[Sample1]
[Sample2]
[Sample3]
[Sample4]
[Sample5]
public class UserService
{
}
このようにAttributeが大量に付いていると、どれが重要なのか判断しにくくなります。
Attributeは、設定やメタデータを簡潔に表現できる反面、処理の流れが見えにくくなることもあります。
本当に必要な情報だけをAttributeとして定義することが大切です。
8-4. リフレクションはパフォーマンスに注意する
Attributeを実行時に取得する場合、多くはリフレクションを使います。
リフレクションは便利ですが、通常のコードに比べて処理コストが高くなることがあります。
特に、大量のクラスやプロパティを毎回走査するような処理では注意が必要です。
必要に応じて、取得したAttribute情報をキャッシュする、起動時に一度だけ読み込む、対象を絞って検索するなどの工夫をしましょう。
8-5. ビジネスロジックをAttributeに詰め込みすぎない
Attributeは、あくまで追加情報を表すためのものです。
複雑な業務処理やビジネスロジックをAttributeに詰め込みすぎると、コードの見通しが悪くなります。
悪い例として、Attributeの中でデータベースアクセスや複雑な計算を行うような設計は避けたほうがよいです。
Attributeは「目印」や「設定値」として使い、実際の処理はサービスクラスや専用の処理クラスに分けると、保守しやすいコードになります。
9. 初心者向け:Attributeの理解を深めるサンプルコード
9-1. Obsoleteを使った警告表示のサンプル
まずは、標準AttributeであるObsoleteのサンプルです。
C#using System;
public class Sample
{
[Obsolete("OldMethodは古いメソッドです。NewMethodを使用してください。")]
public void OldMethod()
{
Console.WriteLine("OldMethodが呼び出されました");
}
public void NewMethod()
{
Console.WriteLine("NewMethodが呼び出されました");
}
}
public class Program
{
public static void Main()
{
var sample = new Sample();
sample.OldMethod();
}
}
このコードでは、OldMethodに[Obsolete]を付けています。
そのため、sample.OldMethod()を呼び出すと、コンパイル時に警告が表示されます。
9-2. Requiredを使った入力チェックのサンプル
次に、Requiredを使ったサンプルです。
C#using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
public class User
{
[Required(ErrorMessage = "名前は必須です")]
public string Name { get; set; }
}
public class Program
{
public static void Main()
{
var user = new User();
var context = new ValidationContext(user);
var results = new List<ValidationResult>();
bool isValid = Validator.TryValidateObject(
user,
context,
results,
true
);
Console.WriteLine(isValid);
foreach (var result in results)
{
Console.WriteLine(result.ErrorMessage);
}
}
}
この例では、Nameに値を設定していないため、バリデーションエラーになります。
[Required]を付けるだけではなく、Validator.TryValidateObjectで検証を実行している点が重要です。
9-3. カスタムAttributeを定義するサンプル
次に、独自のAttributeを作ってみます。
C#using System;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class DescriptionAttribute : Attribute
{
public string Text { get; }
public DescriptionAttribute(string text)
{
Text = text;
}
}
このDescriptionAttributeは、クラスやメソッドに説明文を付けるためのAttributeです。
AttributeUsageによって、クラスとメソッドにだけ付けられるようにしています。
9-4. リフレクションでカスタムAttributeを読み取るサンプル
作成したカスタムAttributeをクラスに付けて、リフレクションで読み取ってみます。
C#using System;
using System.Reflection;
[Description("ユーザー情報を扱うサービスです")]
public class UserService
{
}
public class Program
{
public static void Main()
{
var attribute = typeof(UserService)
.GetCustomAttribute<DescriptionAttribute>();
if (attribute != null)
{
Console.WriteLine(attribute.Text);
}
}
}
このコードでは、UserServiceに付けたDescriptionAttributeを取得しています。
取得したAttributeのTextプロパティを表示することで、Attributeに設定した値を利用できます。
9-5. サンプルコードの実行結果と解説
上記のカスタムAttributeのサンプルを実行すると、次のように表示されます。
ユーザー情報を扱うサービスです
この結果から、クラスに付けたAttributeの値を、実行時に取得できていることがわかります。
流れを整理すると、次のようになります。
C#[Description("ユーザー情報を扱うサービスです")]
public class UserService
{
}
まず、クラスにAttributeを付けます。
次に、リフレクションでAttributeを取得します。
C#var attribute = typeof(UserService)
.GetCustomAttribute<DescriptionAttribute>();
最後に、取得したAttributeの値を使います。
C#Console.WriteLine(attribute.Text);
このように、Attributeは「コードに追加情報を付ける」「その情報を実行時に読み取る」「読み取った情報を処理に利用する」という流れで使われます。
10. C#のアノテーション・Attributeに関するよくある質問
10-1. C#にアノテーションという機能はある?
C#には、Javaのように「アノテーション」という名前の機能はありません。
ただし、C#のAttributeはJavaのアノテーションに近い役割を持っています。
そのため、C#で「アノテーション」と言われる場合は、多くの場合Attributeを意味しています。
10-2. Attributeとコメントの違いは?
コメントは、人間が読むための説明です。
C#// このメソッドは古いです
public void OldMethod()
{
}
一方、Attributeはプログラムやコンパイラ、フレームワークが読み取れるメタデータです。
C#[Obsolete("このメソッドは古いです")]
public void OldMethod()
{
}
コメントは基本的に実行時の処理に影響しませんが、Attributeはリフレクションやフレームワークによって読み取られ、処理に利用されることがあります。
10-3. Attributeとデコレーターの違いは?
デコレーターという言葉は、言語や文脈によって意味が異なります。
たとえば、PythonやTypeScriptでは、関数やクラスの動作を変更する仕組みとしてデコレーターがあります。
C#のAttributeは、基本的には動作を直接変更するものではなく、メタデータを付ける仕組みです。
ただし、フレームワークがAttributeを読み取って処理を変えることはあります。
そのため、見た目や用途が似ている場面はありますが、C#のAttribute自体は処理を包み込んで変更するものではない、と理解するとよいでしょう。
10-4. Attributeを付けるだけで処理が動く?
基本的には、Attributeを付けるだけでは処理は動きません。
Attributeはメタデータです。
そのメタデータを読み取るコンパイラ、フレームワーク、ライブラリ、または自作の処理があって初めて意味を持ちます。
たとえば、[Required]は必須入力を表しますが、実際に入力チェックを行うにはバリデーション処理が必要です。
10-5. 初心者はどのAttributeから覚えるべき?
初心者は、まず次のAttributeから覚えるのがおすすめです。
Obsoleteは、Attributeの基本的な動きを理解しやすいです。
C#[Obsolete]
public void OldMethod()
{
}
RequiredやStringLengthは、入力チェックでよく使われます。
C#[Required]
[StringLength(20)]
public string Name { get; set; }
Displayは、画面表示やエラーメッセージと関係するため、Webアプリケーション開発でよく登場します。
C#[Display(Name = "ユーザー名")]
public string Name { get; set; }
最初は、Obsolete、Required、StringLength、Displayあたりから理解すると、C#のAttributeの使い方をつかみやすくなります。
まとめ
C#のアノテーションとは、多くの場合Attribute(属性)のことを指します。
Attributeは、クラス、メソッド、プロパティなどに追加情報を付けるための仕組みです。[Obsolete]のようにコンパイラへ情報を伝えたり、[Required]のようにバリデーション情報を指定したり、[Route]のようにフレームワークの設定を宣言したりできます。
C#のAttributeの基本は、角括弧[]で対象の直前に書くことです。
C#[Obsolete]
public void OldMethod()
{
}
また、独自のAttributeを作成することもできます。
C#public class SampleAttribute : Attribute
{
}
作成したAttributeは、リフレクションを使って実行時に取得できます。
C#var attribute = typeof(SampleClass)
.GetCustomAttribute<SampleAttribute>();
ただし、Attributeはあくまでメタデータであり、付けただけで処理が自動的に実行されるわけではありません。Attributeを読み取って利用する仕組みが必要です。
初心者のうちは、まずObsolete、Required、StringLength、Displayなどの標準Attributeから覚えるとよいでしょう。その後、カスタムAttributeやリフレクションを学ぶことで、C#のアノテーションにあたるAttributeをより実践的に使えるようになります。

