C# Attribute(属性)とは?基本の使い方から自作・リフレクション活用まで初心者向けに徹底解説

はじめに

C#で開発をしていると、クラスやメソッドの上に次のような記述を見かけることがあります。

C#
[Obsolete]
public void OldMethod()
{
}

この角括弧 [] で書かれているものが、C#の**Attribute(属性)**です。

Attributeは、コードそのものに「追加情報」を与えるための仕組みです。たとえば「このメソッドは非推奨です」「このクラスはシリアライズできます」「このプロパティは必須項目です」といった情報を、クラス・メソッド・プロパティなどに付与できます。

C# Attributeは、ASP.NET、Entity Framework、テストフレームワーク、シリアライズ、バリデーションなど、実務でも非常によく使われます。一方で、初心者にとっては「付けると何が起きるのか」「コメントと何が違うのか」「自作する意味はあるのか」が分かりにくい部分でもあります。

この記事では、C# Attributeの基本概念から、標準Attributeの使い方、自作Attribute、リフレクションによる取得方法、実務での活用例まで、初心者にも分かりやすく解説します。

1. C# Attribute(属性)とは?まず押さえる基本概念

C#のAttributeを理解するうえで大切なのは、Attributeは「処理そのもの」ではなく「コードに付ける追加情報」であるという点です。

Attributeを付けることで、コンパイラ、フレームワーク、実行時の処理、開発ツールなどに対して、特定の意味やルールを伝えることができます。

1-1. Attributeはクラス・メソッド・プロパティなどに付与するメタデータ

Attributeは、C#のクラス、メソッド、プロパティ、フィールド、引数、戻り値、アセンブリなどに付与できるメタデータです。

メタデータとは、簡単に言えば「データについての情報」です。C#のコードにおけるAttributeは、クラスやメソッドなどに対して「この要素はどのような意味を持つのか」を補足する情報だと考えると分かりやすいでしょう。

たとえば、次のコードでは OldMethod メソッドに Obsolete Attributeを付けています。

C#
[Obsolete]
public void OldMethod()
{
Console.WriteLine("古いメソッドです");
}

この場合、OldMethod は「非推奨のメソッドである」というメタデータを持つことになります。

Attributeは通常のコードのように直接処理を実行するものではありません。しかし、コンパイラやフレームワークがその情報を読み取り、警告を出したり、実行時の動作を変えたりするために使われます。

1-2. Attributeを使うとコードに追加情報や振る舞いのヒントを持たせられる

Attributeを使うと、コードに対して「このように扱ってほしい」というヒントを与えることができます。

たとえば、次のような用途があります。

C#
[Obsolete("このメソッドは非推奨です。NewMethodを使用してください。")]
public void OldMethod()
{
}

[Serializable]
public class User
{
public string Name { get; set; }
}

Obsolete Attributeは、対象のメソッドが非推奨であることを示します。これにより、そのメソッドを使用したときにコンパイラが警告を出せます。

Serializable Attributeは、そのクラスがシリアライズ可能であることを示します。これにより、シリアライズ処理を行う仕組みがクラスの扱い方を判断できます。

つまり、Attributeは「コードに意味を追加するための印」のようなものです。

1-3. コメントや通常の変数との違い

Attributeはコメントと似ているように見えるかもしれません。しかし、コメントとは大きく異なります。

コメントは人間が読むための説明です。

C#
// このメソッドは非推奨です
public void OldMethod()
{
}

このコメントは開発者には伝わりますが、コンパイラやフレームワークは通常このコメントを利用しません。

一方、Attributeはプログラムから読み取れる情報です。

C#
[Obsolete("このメソッドは非推奨です")]
public void OldMethod()
{
}

この場合、コンパイラは Obsolete Attributeを認識し、このメソッドを使用したコードに対して警告を出せます。

また、通常の変数は処理の中で値を保持するために使いますが、Attributeはクラスやメソッドなどの定義そのものに追加情報を付けるために使います。

つまり、コメントは「人間向けの説明」、変数は「処理中の値」、Attributeは「コード要素に付ける機械的に読み取れる情報」です。

1-4. C#におけるAttributeの代表的な利用シーン

C# Attributeは、さまざまな場面で使われます。

代表的な利用シーンとしては、次のようなものがあります。

C#
[Obsolete]
public void OldMethod()
{
}

非推奨のメソッドを示す場合に使います。

C#
[Required]
public string Name { get; set; }

入力値のバリデーションで、必須項目を示す場合に使います。

C#
[HttpGet]
public IActionResult Index()
{
return View();
}

ASP.NET Core MVCで、HTTP GETリクエストに対応するアクションであることを示す場合に使います。

C#
[TestMethod]
public void AddTest()
{
}

テストメソッドであることを示す場合にもAttributeが使われます。

このように、AttributeはC#のさまざまなフレームワークやライブラリと密接に関わっています。

2. C# Attributeの基本的な書き方

C# Attributeは、対象となるクラスやメソッドなどの直前に、角括弧 [] を使って記述します。

基本的な書き方を理解しておくと、標準Attributeだけでなく、自作Attributeも扱いやすくなります。

2-1. Attributeを付与する基本構文

Attributeの基本構文は次のとおりです。

C#
[Attribute名]
対象の宣言

たとえば、メソッドに Obsolete Attributeを付ける場合は、次のように書きます。

C#
[Obsolete]
public void OldMethod()
{
}

クラスに付ける場合は次のようになります。

C#
[Serializable]
public class User
{
}

プロパティに付ける場合は次のようになります。

C#
[Required]
public string Name { get; set; }

Attributeは「このクラスに対して」「このメソッドに対して」「このプロパティに対して」というように、直後の宣言に適用されます。

2-2. 角括弧[]を使った指定方法

C# Attributeは角括弧 [] の中に記述します。

C#
[Obsolete]
public void OldMethod()
{
}

引数を指定する場合は、通常のメソッド呼び出しのように丸括弧 () を使います。

C#
[Obsolete("このメソッドは非推奨です")]
public void OldMethod()
{
}

複数の引数や名前付き引数を指定することもできます。

C#
[Obsolete("このメソッドは非推奨です", true)]
public void OldMethod()
{
}

この例では、2つ目の引数に true を指定しています。Obsolete Attributeの場合、true を指定すると、このメソッドを使用したときに警告ではなくコンパイルエラーになります。

2-3. Attribute名の「Attribute」省略ルール

C#でAttributeクラスを定義するときは、慣習としてクラス名の末尾に Attribute を付けます。

たとえば、Obsolete Attributeの正式なクラス名は ObsoleteAttribute です。

しかし、使用するときは末尾の Attribute を省略できます。

C#
[Obsolete]
public void OldMethod()
{
}

これは、次のように書くこともできます。

C#
[ObsoleteAttribute]
public void OldMethod()
{
}

どちらも同じ意味です。

ただし、一般的には Attribute を省略して書くことが多いです。

自作Attributeでも同じルールが使えます。

C#
public class SampleAttribute : Attribute
{
}

このように定義した場合、使用時は次のように書けます。

C#
[Sample]
public class MyClass
{
}

もちろん、次のように正式名で書いても問題ありません。

C#
[SampleAttribute]
public class MyClass
{
}

2-4. 複数のAttributeを指定する方法

1つの対象に複数のAttributeを付けることもできます。

書き方は主に2通りあります。

1つ目は、Attributeを複数行に分けて書く方法です。

C#
[Serializable]
[Obsolete("このクラスは古い形式です")]
public class OldUser
{
}

2つ目は、1つの角括弧の中にカンマ区切りで書く方法です。

C#
[Serializable, Obsolete("このクラスは古い形式です")]
public class OldUser
{
}

どちらも同じ意味です。

読みやすさを重視する場合は、複数行に分けて書くことが多いです。特に引数があるAttributeを複数指定する場合は、1行にまとめるよりも分けて書いた方が見やすくなります。

2-5. Attributeを付けられる対象一覧

C# Attributeは、さまざまな対象に付けられます。

代表的な対象は次のとおりです。

C#
[Serializable]
public class User
{
}

クラスに付ける例です。

C#
[Obsolete]
public void OldMethod()
{
}

メソッドに付ける例です。

C#
[Required]
public string Name { get; set; }

プロパティに付ける例です。

C#
[NonSerialized]
private string cache;

フィールドに付ける例です。

C#
public void SetName([NotNull] string name)
{
}

引数に付ける例です。

C#
[assembly: AssemblyTitle("Sample Application")]

アセンブリに付ける例です。

Attributeをどの対象に付けられるかは、そのAttributeの定義によって異なります。たとえば、クラスにだけ付けられるAttributeもあれば、メソッドやプロパティにも付けられるAttributeもあります。

この適用対象を制御するために使うのが AttributeUsage Attributeです。

3. よく使われる標準Attributeの例

C#には、あらかじめ用意されている標準Attributeが多数あります。

ここでは、初心者が特に目にしやすい代表的なAttributeを紹介します。

3-1. Obsolete属性:非推奨メンバーを知らせる

Obsolete Attributeは、クラス、メソッド、プロパティなどが非推奨であることを示すために使います。

C#
[Obsolete]
public void OldMethod()
{
}

このメソッドを別の場所で呼び出すと、コンパイラが警告を出します。

C#
OldMethod();

警告メッセージを指定することもできます。

C#
[Obsolete("OldMethodは非推奨です。NewMethodを使用してください。")]
public void OldMethod()
{
}

public void NewMethod()
{
}

さらに、使用を警告ではなくエラーにすることもできます。

C#
[Obsolete("このメソッドは使用できません。NewMethodを使用してください。", true)]
public void OldMethod()
{
}

第2引数に true を指定すると、このメソッドを使ったコードはコンパイルエラーになります。

ライブラリやAPIを保守しているときに、古い機能を段階的に廃止したい場合によく使われます。

3-2. Serializable属性:シリアライズ可能であることを示す

Serializable Attributeは、クラスがシリアライズ可能であることを示すために使われます。

C#
[Serializable]
public class User
{
public string Name { get; set; }
public int Age { get; set; }
}

シリアライズとは、オブジェクトをファイル保存や通信に適した形式に変換することです。逆に、保存されたデータからオブジェクトに戻すことをデシリアライズと呼びます。

Serializable Attributeは、主に従来の.NETのシリアライズ機能などで使われます。

ただし、現代のC#開発では、JSONシリアライズに System.Text.JsonNewtonsoft.Json を使うことが多く、必ずしもすべてのシリアライズ処理で Serializable Attributeが必要になるわけではありません。

そのため、使用するシリアライズライブラリがどのAttributeを参照するのかを確認することが大切です。

3-3. Conditional属性:条件付きでメソッド呼び出しを有効にする

Conditional Attributeは、特定のコンパイルシンボルが定義されている場合だけ、メソッド呼び出しを有効にするために使います。

よくある例は、デバッグ時だけログを出力する処理です。

C#
using System.Diagnostics;

public class Logger
{
[Conditional("DEBUG")]
public void DebugLog(string message)
{
Console.WriteLine(message);
}
}

この場合、DEBUG シンボルが定義されているビルドでは DebugLog の呼び出しが有効になります。一方、DEBUG シンボルが定義されていないビルドでは、呼び出し自体がコンパイル結果から除外されます。

C#
var logger = new Logger();
logger.DebugLog("デバッグログです");

Conditional Attributeは、デバッグ用ログ、診断処理、トレース処理などで使われます。

注意点として、Conditional Attributeはメソッド自体の実行可否を実行時に判定するものではなく、コンパイル時にメソッド呼び出しを含めるかどうかを制御します。

3-4. AttributeUsage属性:自作Attributeの使用ルールを定義する

AttributeUsage Attributeは、自作Attributeをどこに付けられるか、複数回付けられるか、継承先にも引き継がれるかを指定するために使います。

C#
[AttributeUsage(AttributeTargets.Class)]
public class SampleAttribute : Attribute
{
}

この例では、SampleAttribute はクラスにだけ付けられます。

C#
[Sample]
public class MyClass
{
}

複数の対象を指定することもできます。

C#
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class SampleAttribute : Attribute
{
}

この場合、クラスとメソッドに付けられます。

複数回の指定を許可する場合は、AllowMultiple を使います。

C#
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class TagAttribute : Attribute
{
public string Name { get; }

public TagAttribute(string name)
{
Name = name;
}
}

これにより、同じクラスに複数の Tag Attributeを付けられます。

C#
[Tag("重要")]
[Tag("顧客管理")]
public class Customer
{
}

自作Attributeを作るときは、AttributeUsage を使って適用ルールを明確にしておくのが基本です。

3-5. ASP.NETやEntity Frameworkで使われるAttributeの例

C# Attributeは、ASP.NETやEntity Frameworkなどのフレームワークで非常によく使われます。

ASP.NET Core MVCでは、ルーティングやHTTPメソッドの指定にAttributeを使います。

C#
[Route("users")]
public class UsersController : Controller
{
[HttpGet]
public IActionResult Index()
{
return View();
}

[HttpPost]
public IActionResult Create(User user)
{
return View();
}
}

Route はURLルーティングを指定し、HttpGetHttpPost は対応するHTTPメソッドを指定します。

Entity Frameworkでは、テーブル名や主キー、必須項目などをAttributeで指定できます。

C#
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

[Table("Users")]
public class User
{
[Key]
public int Id { get; set; }

[Required]
[MaxLength(100)]
public string Name { get; set; }
}

Table はテーブル名、Key は主キー、Required は必須、MaxLength は最大文字数を表します。

このように、Attributeを使うことで、クラスやプロパティに対してフレームワークが必要とする設定情報を直接記述できます。

4. C# Attributeの基本的な使い方をコード例で理解する

ここからは、C# Attributeの使い方を具体的なコード例で確認していきます。

クラス、メソッド、プロパティにAttributeを付けるパターンを理解しておくと、標準Attributeも自作Attributeも扱いやすくなります。

4-1. クラスにAttributeを付与する例

クラスにAttributeを付ける場合は、クラス宣言の直前に書きます。

C#
[Serializable]
public class User
{
public string Name { get; set; }
public int Age { get; set; }
}

この例では、User クラスに Serializable Attributeを付けています。

また、ASP.NET Core MVCのコントローラーでは、次のようにクラスにルーティング情報を付けることがあります。

C#
[Route("api/users")]
public class UsersController : ControllerBase
{
}

この場合、UsersControllerapi/users というURLに対応するコントローラーとして扱われます。

クラスに付けるAttributeは、そのクラス全体に関する情報を表す場合に使われます。

4-2. メソッドにAttributeを付与する例

メソッドにAttributeを付ける場合は、メソッド宣言の直前に書きます。

C#
[Obsolete("このメソッドは非推奨です。NewMethodを使用してください。")]
public void OldMethod()
{
Console.WriteLine("古い処理です");
}

public void NewMethod()
{
Console.WriteLine("新しい処理です");
}

この例では、OldMethodObsolete Attributeを付けています。

このメソッドを呼び出すと、コンパイラが警告を出します。

ASP.NET Coreでは、アクションメソッドにHTTPメソッドを表すAttributeを付けます。

C#
[HttpGet]
public IActionResult Index()
{
return View();
}

テストコードでは、テスト対象のメソッドであることを示すAttributeを付けることがあります。

C#
[TestMethod]
public void Add_ReturnsCorrectResult()
{
}

メソッドに付けるAttributeは、「このメソッドをどう扱うか」をフレームワークやツールに伝えるために使われます。

4-3. プロパティにAttributeを付与する例

プロパティにAttributeを付ける場合は、プロパティ宣言の直前に書きます。

C#
public class User
{
[Required]
public string Name { get; set; }

[MaxLength(100)]
public string Email { get; set; }
}

この例では、Name プロパティに RequiredEmail プロパティに MaxLength Attributeを付けています。

これらは、入力値のバリデーションやデータベースのマッピングなどで使われます。

Entity Frameworkでは、次のように主キーを表すAttributeを使うことがあります。

C#
public class Product
{
[Key]
public int Id { get; set; }

[Required]
public string Name { get; set; }
}

プロパティに付けるAttributeは、フォーム入力、データベース、JSON変換など、データの扱いに関する情報を指定する場面でよく使われます。

4-4. 引数付きAttributeの使い方

Attributeには、引数を渡せるものがあります。

C#
[Obsolete("このメソッドは非推奨です")]
public void OldMethod()
{
}

この例では、Obsolete Attributeにメッセージ文字列を渡しています。

自作Attributeでも、コンストラクタを定義することで引数を受け取れます。

C#
public class DisplayNameAttribute : Attribute
{
public string Name { get; }

public DisplayNameAttribute(string name)
{
Name = name;
}
}

このAttributeは、次のように使えます。

C#
[DisplayName("ユーザー名")]
public string UserName { get; set; }

Attributeの引数は、そのAttributeが持つ追加情報として保存されます。そして、リフレクションなどを使って後から読み取ることができます。

4-5. 名前付き引数を使ったAttributeの指定方法

Attributeでは、コンストラクタ引数のほかに、プロパティを使った名前付き引数も指定できます。

C#
public class DisplayAttribute : Attribute
{
public string Name { get; }
public int Order { get; set; }
public bool IsVisible { get; set; }

public DisplayAttribute(string name)
{
Name = name;
}
}

このAttributeは、次のように使えます。

C#
[Display("ユーザー名", Order = 1, IsVisible = true)]
public string UserName { get; set; }

"ユーザー名" はコンストラクタ引数です。Order = 1IsVisible = true は名前付き引数です。

名前付き引数を使うと、必須の値はコンストラクタで受け取り、任意の設定はプロパティで指定する、という設計ができます。

Attributeを自作するときは、必ず指定してほしい値をコンストラクタ引数にし、必要に応じて指定する値をプロパティにするのが分かりやすい設計です。

5. Attributeを自作する方法

C#では、標準で用意されているAttributeだけでなく、自分で独自のAttributeを作ることもできます。

自作Attributeを使うと、アプリケーション固有のメタデータをクラスやメソッドに付与できます。

5-1. Attributeクラスを継承して独自属性を作成する

自作Attributeを作るには、System.Attribute クラスを継承します。

C#
public class MyAttribute : Attribute
{
}

Attributeクラス名は、慣習として末尾に Attribute を付けます。

C#
public class ImportantAttribute : Attribute
{
}

使用するときは、末尾の Attribute を省略できます。

C#
[Important]
public class CustomerService
{
}

これは次のように書くのと同じです。

C#
[ImportantAttribute]
public class CustomerService
{
}

自作Attributeは、ただ定義しただけでは特別な処理を行いません。リフレクションやフレームワーク側の処理で読み取って初めて活用できます。

5-2. 自作Attributeの基本コード例

最もシンプルな自作Attributeの例を見てみましょう。

C#
using System;

public class ImportantAttribute : Attribute
{
}

このAttributeをクラスに付けます。

C#
[Important]
public class CustomerService
{
public void Register()
{
Console.WriteLine("顧客を登録します");
}
}

この状態では、CustomerService に「重要である」という印を付けただけです。

リフレクションを使えば、このクラスに ImportantAttribute が付いているかどうかを判定できます。

C#
var type = typeof(CustomerService);
bool isImportant = Attribute.IsDefined(type, typeof(ImportantAttribute));

Console.WriteLine(isImportant);

このコードでは、CustomerServiceImportantAttribute が付いているため、true が出力されます。

5-3. コンストラクタ引数を持つAttributeを作る

Attributeに値を持たせたい場合は、コンストラクタを定義します。

C#
public class RoleAttribute : Attribute
{
public string RoleName { get; }

public RoleAttribute(string roleName)
{
RoleName = roleName;
}
}

このAttributeは、次のように使えます。

C#
[Role("Admin")]
public class AdminController
{
}

"Admin" という値が、RoleAttributeRoleName に保存されます。

リフレクションで取得すると、Attributeに指定した値を読み取れます。

C#
var type = typeof(AdminController);
var attribute = (RoleAttribute?)Attribute.GetCustomAttribute(type, typeof(RoleAttribute));

if (attribute != null)
{
Console.WriteLine(attribute.RoleName);
}

この例では、Admin が出力されます。

コンストラクタ引数は、そのAttributeを使ううえで必須の値を表す場合に向いています。

5-4. プロパティを持つAttributeを作る

Attributeには、プロパティを持たせることもできます。

C#
public class ApiInfoAttribute : Attribute
{
public string Name { get; }
public string Version { get; set; }
public bool IsPublic { get; set; }

public ApiInfoAttribute(string name)
{
Name = name;
}
}

このAttributeは、次のように使えます。

C#
[ApiInfo("User API", Version = "1.0", IsPublic = true)]
public class UserApi
{
}

Name はコンストラクタ引数として指定し、VersionIsPublic は名前付き引数として指定しています。

プロパティを使うと、任意項目を柔軟に設定できます。

必須項目はコンストラクタ、任意項目はプロパティにすることで、Attributeを使う側にとって分かりやすくなります。

5-5. AttributeUsageで適用対象や複数指定の可否を制御する

自作Attributeを作るときは、AttributeUsage を使って適用対象を明確にするのがおすすめです。

C#
[AttributeUsage(AttributeTargets.Class)]
public class ImportantAttribute : Attribute
{
}

この場合、ImportantAttribute はクラスにだけ付けられます。

メソッドにも付けられるようにするには、次のようにします。

C#
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class ImportantAttribute : Attribute
{
}

複数回付けられるようにするには、AllowMultiple = true を指定します。

C#
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class TagAttribute : Attribute
{
public string Name { get; }

public TagAttribute(string name)
{
Name = name;
}
}

使用例は次のとおりです。

C#
[Tag("Customer")]
[Tag("Important")]
public class CustomerService
{
}

継承先にもAttributeを引き継ぐかどうかは、Inherited で制御できます。

C#
[AttributeUsage(AttributeTargets.Class, Inherited = true)]
public class AuditableAttribute : Attribute
{
}

AttributeUsageを適切に設定しておくと、間違った場所にAttributeを付けてしまうミスを防げます。

5-6. 自作Attributeを使うと便利なケース

自作Attributeは、アプリケーション独自のルールや情報をコードに持たせたい場合に便利です。

たとえば、画面に表示する項目名をプロパティに持たせるケースがあります。

C#
public class DisplayNameAttribute : Attribute
{
public string Name { get; }

public DisplayNameAttribute(string name)
{
Name = name;
}
}
C#
public class User
{
[DisplayName("ユーザーID")]
public int Id { get; set; }

[DisplayName("ユーザー名")]
public string Name { get; set; }
}

また、ログ出力対象のメソッドに印を付けることもできます。

C#
[AttributeUsage(AttributeTargets.Method)]
public class LogAttribute : Attribute
{
}
C#
public class OrderService
{
[Log]
public void CreateOrder()
{
}
}

そのほか、権限チェック、監査ログ、CSV出力項目、APIドキュメント生成、マッピング情報の管理などにも使えます。

ただし、自作Attributeは「付けただけ」では動作しません。必ず、そのAttributeを読み取って処理する仕組みが必要です。

6. リフレクションでAttributeを取得・活用する方法

Attributeは、リフレクションを使って実行時に取得できます。

自作Attributeを活用する場合、リフレクションによる読み取り処理は非常に重要です。

6-1. リフレクションとは何か

リフレクションとは、実行中のプログラムから型情報を調べる仕組みです。

たとえば、クラス名、メソッド名、プロパティ一覧、Attribute情報などを取得できます。

C#
Type type = typeof(User);
Console.WriteLine(type.Name);

このコードでは、User クラスの型情報を取得し、クラス名を出力しています。

リフレクションを使うと、通常のコードでは直接意識しないメタデータを取得できます。Attributeもメタデータの一種なので、リフレクションで読み取れます。

6-2. GetCustomAttributesでAttribute情報を取得する

Attributeを取得する代表的な方法が GetCustomAttributes です。

C#
var attributes = typeof(User).GetCustomAttributes(false);

このコードでは、User クラスに付与されたAttributeの一覧を取得します。

特定のAttributeだけを取得したい場合は、次のようにします。

C#
var attribute = Attribute.GetCustomAttribute(
typeof(User),
typeof(DisplayNameAttribute)
);

ジェネリックメソッドを使うと、より分かりやすく書けます。

C#
var attribute = typeof(User)
.GetCustomAttributes(typeof(DisplayNameAttribute), false)
.FirstOrDefault() as DisplayNameAttribute;

.NETでは、拡張メソッドを使って次のように書くこともあります。

C#
var attribute = typeof(User).GetCustomAttribute<DisplayNameAttribute>();

Attributeを取得した後は、そのプロパティの値を読み取り、処理に利用できます。

6-3. クラスに付与されたAttributeを読み取る

クラスに付与されたAttributeを読み取る例を見てみましょう。

まず、Attributeを定義します。

C#
[AttributeUsage(AttributeTargets.Class)]
public class TableNameAttribute : Attribute
{
public string Name { get; }

public TableNameAttribute(string name)
{
Name = name;
}
}

次に、クラスにAttributeを付けます。

C#
[TableName("Users")]
public class User
{
public int Id { get; set; }
public string Name { get; set; }
}

リフレクションで読み取ります。

C#
var type = typeof(User);
var attribute = type.GetCustomAttribute<TableNameAttribute>();

if (attribute != null)
{
Console.WriteLine(attribute.Name);
}

このコードでは、Users が出力されます。

このように、クラスに付けたAttributeを使えば、テーブル名、画面名、API名、カテゴリ名などを管理できます。

6-4. メソッドやプロパティのAttributeを読み取る

メソッドに付与されたAttributeを読み取るには、まず対象のメソッド情報を取得します。

C#
[AttributeUsage(AttributeTargets.Method)]
public class LogAttribute : Attribute
{
public string Message { get; }

public LogAttribute(string message)
{
Message = message;
}
}
C#
public class OrderService
{
[Log("注文作成処理を実行します")]
public void CreateOrder()
{
}
}

リフレクションでメソッドのAttributeを取得します。

C#
var type = typeof(OrderService);
var method = type.GetMethod("CreateOrder");

var attribute = method?.GetCustomAttribute<LogAttribute>();

if (attribute != null)
{
Console.WriteLine(attribute.Message);
}

プロパティに付与されたAttributeを取得する場合は、GetProperty を使います。

C#
public class User
{
[DisplayName("ユーザー名")]
public string Name { get; set; }
}
C#
var property = typeof(User).GetProperty("Name");
var attribute = property?.GetCustomAttribute<DisplayNameAttribute>();

if (attribute != null)
{
Console.WriteLine(attribute.Name);
}

このように、メソッドやプロパティに付けたAttributeも、リフレクションで取得して処理に活用できます。

6-5. 自作Attributeの値を取得して処理に活用する

自作Attributeの値を使って、実際の処理を変える例を見てみましょう。

ここでは、プロパティに表示名を付け、それを画面表示やCSV出力に利用するケースを考えます。

C#
[AttributeUsage(AttributeTargets.Property)]
public class CsvColumnAttribute : Attribute
{
public string HeaderName { get; }
public int Order { get; set; }

public CsvColumnAttribute(string headerName)
{
HeaderName = headerName;
}
}
C#
public class User
{
[CsvColumn("ユーザーID", Order = 1)]
public int Id { get; set; }

[CsvColumn("ユーザー名", Order = 2)]
public string Name { get; set; }

[CsvColumn("メールアドレス", Order = 3)]
public string Email { get; set; }
}

このAttributeを読み取り、CSVヘッダーを作成します。

C#
var properties = typeof(User).GetProperties();

var columns = properties
.Select(p => new
{
Property = p,
Attribute = p.GetCustomAttribute<CsvColumnAttribute>()
})
.Where(x => x.Attribute != null)
.OrderBy(x => x.Attribute!.Order)
.Select(x => x.Attribute!.HeaderName);

var header = string.Join(",", columns);

Console.WriteLine(header);

出力結果は次のようになります。

C#
ユーザーID,ユーザー名,メールアドレス

このように、Attributeに設定情報を持たせ、リフレクションで読み取ることで、共通処理を自動化できます。

6-6. リフレクション利用時の注意点

リフレクションは便利ですが、注意点もあります。

まず、通常のメソッド呼び出しやプロパティアクセスに比べて、リフレクションは処理コストが高くなりやすいです。頻繁に実行される処理で毎回リフレクションを使うと、パフォーマンスに影響する場合があります。

たとえば、Webアプリケーションでリクエストのたびに大量のAttributeを読み取るような処理は注意が必要です。

対策として、取得したAttribute情報をキャッシュする方法があります。

C#
private static readonly Dictionary<Type, object[]> AttributeCache = new();

public static object[] GetAttributes(Type type)
{
if (!AttributeCache.TryGetValue(type, out var attributes))
{
attributes = type.GetCustomAttributes(false);
AttributeCache[type] = attributes;
}

return attributes;
}

また、文字列でメソッド名やプロパティ名を指定する場合、名前変更に弱くなります。

C#
var method = typeof(OrderService).GetMethod("CreateOrder");

このようなコードは、メソッド名を変更したときに実行時までミスに気づきにくい場合があります。

リフレクションは強力ですが、使いすぎるとコードの見通しが悪くなることもあります。必要な場面に絞って使うことが大切です。

7. C# Attributeの実践的な活用例

C# Attributeは、実務でも多くの場面で活用されています。

ここでは、代表的な活用例を見ていきます。

7-1. バリデーション処理にAttributeを使う

Attributeの代表的な活用例がバリデーションです。

たとえば、ユーザー登録フォームの入力値を検証する場合、プロパティにAttributeを付けてルールを表現できます。

C#
using System.ComponentModel.DataAnnotations;

public class UserRegisterModel
{
[Required]
[MaxLength(50)]
public string Name { get; set; }

[Required]
[EmailAddress]
public string Email { get; set; }

[Range(0, 120)]
public int Age { get; set; }
}

この例では、Name は必須かつ最大50文字、Email は必須かつメールアドレス形式、Age は0から120の範囲というルールをAttributeで表しています。

ASP.NET Coreなどでは、これらのAttributeをフレームワークが読み取り、モデルバリデーションに利用します。

バリデーションルールをモデルに近い場所に書けるため、コードの意図が分かりやすくなります。

7-2. ログ出力や監査情報にAttributeを使う

ログ出力や監査情報の制御にもAttributeを使えます。

たとえば、特定のメソッドだけログ出力対象にしたい場合、次のようなAttributeを定義できます。

C#
[AttributeUsage(AttributeTargets.Method)]
public class AuditLogAttribute : Attribute
{
public string ActionName { get; }

public AuditLogAttribute(string actionName)
{
ActionName = actionName;
}
}

メソッドにAttributeを付けます。

C#
public class UserService
{
[AuditLog("ユーザー作成")]
public void CreateUser()
{
}

[AuditLog("ユーザー削除")]
public void DeleteUser()
{
}
}

このAttributeを読み取ることで、「どの操作を監査ログに残すか」「ログ上の表示名をどうするか」を管理できます。

実際のシステムでは、AOP、フィルター、インターセプターなどの仕組みと組み合わせて使われることがあります。

7-3. APIやMVCフレームワークでAttributeを使う

ASP.NET Core MVCやWeb APIでは、Attributeが非常に多く使われます。

C#
[ApiController]
[Route("api/users")]
public class UsersController : ControllerBase
{
[HttpGet("{id}")]
public IActionResult Get(int id)
{
return Ok();
}

[HttpPost]
public IActionResult Create(User user)
{
return Ok();
}
}

ApiController はAPIコントローラーであることを示し、Route はURLルート、HttpGetHttpPost はHTTPメソッドを表します。

認可にもAttributeが使われます。

C#
[Authorize]
public IActionResult MyPage()
{
return View();
}

特定のロールだけに制限することもできます。

C#
[Authorize(Roles = "Admin")]
public IActionResult AdminPage()
{
return View();
}

このように、Attributeを使うと、APIや画面のルーティング、認証・認可、入力検証などを宣言的に記述できます。

7-4. テストコードでAttributeを使う

テストフレームワークでもAttributeはよく使われます。

MSTestでは、テストクラスやテストメソッドを表すためにAttributeを使います。

C#
[TestClass]
public class CalculatorTests
{
[TestMethod]
public void Add_ReturnsSum()
{
var result = 1 + 2;
Assert.AreEqual(3, result);
}
}

xUnitでは、次のように Fact Attributeを使います。

C#
public class CalculatorTests
{
[Fact]
public void Add_ReturnsSum()
{
var result = 1 + 2;
Assert.Equal(3, result);
}
}

NUnitでは、次のように Test Attributeを使います。

C#
[Test]
public void Add_ReturnsSum()
{
var result = 1 + 2;
Assert.That(result, Is.EqualTo(3));
}

テストフレームワークは、Attributeを読み取って「どのメソッドをテストとして実行するか」を判断します。

つまり、Attributeはテスト自動実行の仕組みにも欠かせない存在です。

7-5. 設定やマッピング情報をAttributeで管理する

Attributeは、設定情報やマッピング情報の管理にも使えます。

たとえば、データベースのテーブル名やカラム名を指定する場合です。

C#
[Table("Users")]
public class User
{
[Column("user_id")]
public int Id { get; set; }

[Column("user_name")]
public string Name { get; set; }
}

また、JSONのプロパティ名を指定する場合にもAttributeが使われます。

C#
using System.Text.Json.Serialization;

public class User
{
[JsonPropertyName("user_id")]
public int Id { get; set; }

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

この例では、C#のプロパティ名とJSONの項目名を別々に管理できます。

JSON
{
"user_id": 1,
"user_name": "Taro"
}

Attributeを使うことで、設定ファイルに書くほどではない情報や、対象のクラス・プロパティと密接に関係する情報をコード上で管理できます。

8. C# Attributeを使うメリット・デメリット

C# Attributeは便利な仕組みですが、メリットだけでなくデメリットもあります。

適切に使えばコードの見通しが良くなりますが、多用すると逆に分かりにくくなることもあります。

8-1. コードの意図を明確にできる

Attributeを使う大きなメリットは、コードの意図を明確にできることです。

C#
[Required]
public string Name { get; set; }

このコードを見るだけで、Name が必須項目であることが分かります。

C#
[Obsolete("NewMethodを使用してください")]
public void OldMethod()
{
}

このコードを見ると、OldMethod は古いメソッドであり、代わりに NewMethod を使うべきだと分かります。

コメントでも説明はできますが、Attributeであればフレームワークやツールが機械的に読み取れます。これにより、警告表示、バリデーション、自動生成、テスト実行などにつなげられます。

8-2. フレームワークとの連携がしやすい

C# Attributeは、ASP.NET Core、Entity Framework、テストフレームワーク、シリアライザーなど、さまざまなフレームワークで使われています。

C#
[HttpGet]
public IActionResult Index()
{
return View();
}

このように書くだけで、ASP.NET CoreはこのメソッドをHTTP GETリクエストに対応するアクションとして扱えます。

C#
[Key]
public int Id { get; set; }

Entity Frameworkでは、主キーであることを示せます。

Attributeを使うと、設定をコードの近くに書けるため、クラスやメソッドの役割を把握しやすくなります。

8-3. 共通処理や自動化に活用できる

Attributeは、共通処理や自動化にも向いています。

たとえば、CSV出力対象のプロパティをAttributeで指定すれば、リフレクションで読み取って自動的にCSVを生成できます。

C#
public class User
{
[CsvColumn("ユーザーID", Order = 1)]
public int Id { get; set; }

[CsvColumn("ユーザー名", Order = 2)]
public string Name { get; set; }
}

同じように、ログ出力対象、権限チェック対象、画面表示項目、APIドキュメントの説明などもAttributeで管理できます。

手作業で同じような設定を書く必要が減り、処理の共通化や保守性の向上につながります。

8-4. 多用すると処理の流れが見えにくくなる

Attributeのデメリットは、多用すると処理の流れが見えにくくなることです。

C#
[Authorize]
[ValidateModel]
[AuditLog("ユーザー登録")]
[Transaction]
[Retry]
public IActionResult Create(User user)
{
return View();
}

このように多くのAttributeが並ぶと、実際にどの処理がどの順番で実行されるのか分かりにくくなります。

特に、Attributeを読み取る側の処理が別の場所にある場合、コードを追いかけるのが難しくなります。

Attributeは便利ですが、「付ければ何でも解決する」というものではありません。処理の流れが見えにくくならないように、役割を明確にして使うことが重要です。

8-5. リフレクションによるパフォーマンス面の注意

Attributeの取得には、リフレクションが使われることが多いです。

リフレクションは便利ですが、通常のプロパティアクセスやメソッド呼び出しに比べるとコストが高くなりやすいです。

たとえば、大量のデータを処理するたびに毎回Attributeを取得すると、パフォーマンスに影響する可能性があります。

C#
foreach (var item in items)
{
var attributes = item.GetType().GetCustomAttributes(false);
}

このような処理は、件数が多い場合に注意が必要です。

対策として、Attribute情報を一度取得してキャッシュする方法があります。

C#
private static readonly Dictionary<Type, object[]> Cache = new();

public static object[] GetCachedAttributes(Type type)
{
if (!Cache.TryGetValue(type, out var attributes))
{
attributes = type.GetCustomAttributes(false);
Cache[type] = attributes;
}

return attributes;
}

頻繁に使うAttribute情報はキャッシュし、毎回リフレクションで取得しないようにするのが実務ではよくある工夫です。

9. C# Attributeを使うときの注意点とベストプラクティス

C# Attributeを適切に使うには、いくつかの注意点があります。

特に自作Attributeを作る場合は、設計を誤ると分かりにくいコードになってしまいます。

9-1. Attributeに複雑なロジックを持たせすぎない

Attributeは、基本的にはメタデータを表すためのものです。

そのため、Attribute自体に複雑な処理を持たせすぎないようにしましょう。

C#
public class BadAttribute : Attribute
{
public BadAttribute()
{
// データベース接続
// ファイル読み込み
// 外部API呼び出し
}
}

このような設計は避けるべきです。

Attributeは、あくまで情報を保持する役割にとどめ、実際の処理はAttributeを読み取る側に書くのが基本です。

よい例は次のような形です。

C#
public class AuditLogAttribute : Attribute
{
public string ActionName { get; }

public AuditLogAttribute(string actionName)
{
ActionName = actionName;
}
}

このAttributeは、監査ログのアクション名という情報だけを持っています。

実際にログを出力する処理は、別のクラスやフィルターで実装する方が設計として分かりやすくなります。

9-2. 命名は「〇〇Attribute」にする

自作Attributeのクラス名は、末尾に Attribute を付けるのが一般的です。

C#
public class DisplayNameAttribute : Attribute
{
}

使用するときは、Attribute を省略できます。

C#
[DisplayName]
public class User
{
}

C#ではAttribute名の末尾の Attribute を省略できるため、クラス定義では DisplayNameAttribute、使用時は [DisplayName] と書くのが自然です。

逆に、クラス名に Attribute を付けないと、Attributeであることが分かりにくくなります。

C#
public class DisplayName : Attribute
{
}

このような書き方も可能ですが、慣習から外れるためおすすめしません。

9-3. 適用対象をAttributeUsageで明確にする

自作Attributeを作るときは、AttributeUsage を使って適用対象を明確にしましょう。

C#
[AttributeUsage(AttributeTargets.Property)]
public class DisplayNameAttribute : Attribute
{
public string Name { get; }

public DisplayNameAttribute(string name)
{
Name = name;
}
}

この例では、DisplayNameAttribute はプロパティにだけ付けられます。

もし AttributeUsage を指定しないと、意図しない対象に付けられてしまう可能性があります。

たとえば、プロパティ用のAttributeなのにクラスに付けられてしまうと、読み取り側の処理で想定外の状態になるかもしれません。

適用対象を明確にしておくことで、コードの安全性と可読性が高まります。

9-4. 必要以上に独自Attributeを増やさない

自作Attributeは便利ですが、必要以上に増やしすぎると管理が難しくなります。

たとえば、少しの違いだけで似たようなAttributeをたくさん作ると、どれを使うべきか分かりにくくなります。

C#
[UserName]
[UserDisplayName]
[UserLabel]
[UserTitle]
public string Name { get; set; }

このような状態になると、開発者によって使い方がばらつきやすくなります。

既存の標準AttributeやフレームワークのAttributeで対応できる場合は、まずそれを使うことを検討しましょう。

自作Attributeを作るのは、アプリケーション固有の意味を表したい場合や、共通処理と組み合わせて明確なメリットがある場合に絞るのがおすすめです。

9-5. 読み取り側の処理もセットで設計する

Attributeは、付けただけでは基本的に何も起きません。

そのAttributeを読み取る処理があって初めて意味を持ちます。

たとえば、次のようなAttributeを作ったとします。

C#
[AttributeUsage(AttributeTargets.Method)]
public class RetryAttribute : Attribute
{
public int Count { get; }

public RetryAttribute(int count)
{
Count = count;
}
}

メソッドに付けます。

C#
[Retry(3)]
public void Send()
{
}

しかし、RetryAttribute を読み取ってリトライ処理を実行する仕組みがなければ、実際には何もリトライされません。

Attributeを設計するときは、必ず次の点を考える必要があります。

どこに付けるのか、何の情報を持つのか、誰が読み取るのか、読み取った後に何をするのか。

この4つが明確になっていないAttributeは、後から意味が分かりにくくなりがちです。

10. 初心者がつまずきやすいC# Attributeの疑問

C# Attributeは、最初は仕組みが少し分かりにくい機能です。

ここでは、初心者がつまずきやすい疑問を整理して解説します。

10-1. Attributeを付けるだけで処理は自動実行されるのか

基本的に、Attributeを付けるだけで処理が自動実行されるわけではありません。

たとえば、次のようなAttributeを作ったとします。

C#
public class LogAttribute : Attribute
{
}

そしてメソッドに付けます。

C#
[Log]
public void CreateUser()
{
}

この状態だけでは、ログ出力は行われません。

ログを出すためには、LogAttribute が付いているメソッドを検出し、ログを出力する仕組みが必要です。

一方で、ObsoleteHttpGet のような標準AttributeやフレームワークのAttributeは、コンパイラやフレームワークが読み取って動作します。

つまり、Attributeの効果は「それを読み取る仕組みが存在するかどうか」によって決まります。

10-2. Attributeとアノテーションの違い

C#では「Attribute」という用語を使います。一方、Javaなどでは「アノテーション」という用語が使われます。

どちらも、コードにメタデータを付与する仕組みという意味では似ています。

C#では次のように書きます。

C#
[Obsolete]
public void OldMethod()
{
}

Javaでは次のように @ を使って書きます。

Java
@Deprecated
public void oldMethod() {
}

概念としては近いですが、言語仕様や書き方、取得方法、実行時の扱いは異なります。

C#を学ぶときは、「C#ではAttributeと呼ぶ」と覚えておくとよいでしょう。

10-3. Attributeとリフレクションの関係

Attributeはメタデータであり、その情報を実行時に読み取るためによく使われるのがリフレクションです。

たとえば、次のようなAttributeがあるとします。

C#
[DisplayName("ユーザー名")]
public string Name { get; set; }

この DisplayName の値を実行時に取得するには、リフレクションを使います。

C#
var property = typeof(User).GetProperty("Name");
var attribute = property?.GetCustomAttribute<DisplayNameAttribute>();

if (attribute != null)
{
Console.WriteLine(attribute.Name);
}

Attributeは「情報を付ける仕組み」、リフレクションは「その情報を読み取る仕組み」と考えると分かりやすいです。

ただし、すべてのAttributeが必ずリフレクションで使われるわけではありません。Obsolete のようにコンパイラが利用するAttributeもあります。

10-4. Attributeはいつ使うべきか

Attributeは、クラスやメソッド、プロパティに対して追加情報を持たせたいときに使います。

特に、次のような場合に向いています。

対象のコード要素と密接に関係する設定を持たせたい場合。

C#
[MaxLength(100)]
public string Name { get; set; }

フレームワークに対して動作のヒントを与えたい場合。

C#
[HttpPost]
public IActionResult Create(User user)
{
return Ok();
}

リフレクションで読み取って共通処理を行いたい場合。

C#
[CsvColumn("ユーザー名")]
public string Name { get; set; }

一方で、単なる処理分岐や一時的な値の保存にはAttributeは向いていません。その場合は、通常の変数、メソッド、設定ファイル、DIコンテナなどを使う方が自然です。

Attributeは「コード要素に意味を付けたいとき」に使うのが基本です。

10-5. 自作Attributeは実務で使われるのか

自作Attributeは実務でも使われます。

ただし、どのプロジェクトでも大量に作るものではありません。多くの場合、標準Attributeやフレームワークが用意しているAttributeで十分です。

自作Attributeが使われやすい場面としては、次のようなものがあります。

ログ出力対象を指定する、監査ログの対象を指定する、権限情報を付与する、CSVやExcelの出力項目を指定する、画面表示名を管理する、独自フレームワークの設定情報を持たせる、といったケースです。

たとえば、CSV出力用のAttributeは実務でも分かりやすい例です。

C#
[CsvColumn("商品名", Order = 1)]
public string ProductName { get; set; }

このように、Attributeを使うことで、出力項目や表示名をプロパティの近くに記述できます。

ただし、自作Attributeを作る場合は、それを読み取って活用する処理まで含めて設計することが重要です。

11. C# Attributeに関するよくある質問

ここでは、C# Attributeについてよくある質問をまとめます。

11-1. C#のAttributeとは何ですか?

C#のAttributeとは、クラス、メソッド、プロパティなどに追加情報を付与するための仕組みです。

たとえば、次のコードでは OldMethodObsolete Attributeを付けています。

C#
[Obsolete("このメソッドは非推奨です")]
public void OldMethod()
{
}

このAttributeにより、OldMethod が非推奨であることをコンパイラに伝えられます。

Attributeはコメントとは違い、コンパイラ、フレームワーク、リフレクションなどから機械的に読み取れます。

11-2. Attributeはどこに付けられますか?

Attributeは、クラス、メソッド、プロパティ、フィールド、引数、戻り値、アセンブリなどに付けられます。

たとえば、クラスに付ける場合は次のように書きます。

C#
[Serializable]
public class User
{
}

メソッドに付ける場合は次のように書きます。

C#
[Obsolete]
public void OldMethod()
{
}

プロパティに付ける場合は次のように書きます。

C#
[Required]
public string Name { get; set; }

ただし、すべてのAttributeがすべての対象に付けられるわけではありません。Attributeごとに適用可能な対象が決まっています。

自作Attributeでは、AttributeUsage を使って適用対象を指定できます。

11-3. 標準Attributeと自作Attributeの違いは何ですか?

標準Attributeは、C#や.NET、フレームワークがあらかじめ用意しているAttributeです。

たとえば、ObsoleteSerializableConditionalAttributeUsage などがあります。

C#
[Obsolete]
public void OldMethod()
{
}

自作Attributeは、開発者が独自に作成するAttributeです。

C#
public class ImportantAttribute : Attribute
{
}
C#
[Important]
public class CustomerService
{
}

標準Attributeは、コンパイラやフレームワークが最初から意味を理解している場合があります。

一方、自作Attributeは、作っただけでは特別な動作をしません。リフレクションなどで読み取り、処理に活用する仕組みを自分で用意する必要があります。

11-4. Attributeの値はどうやって取得しますか?

Attributeの値は、主にリフレクションを使って取得します。

たとえば、次のような自作Attributeがあるとします。

C#
public class DisplayNameAttribute : Attribute
{
public string Name { get; }

public DisplayNameAttribute(string name)
{
Name = name;
}
}

プロパティに付けます。

C#
public class User
{
[DisplayName("ユーザー名")]
public string Name { get; set; }
}

リフレクションで取得します。

C#
var property = typeof(User).GetProperty("Name");
var attribute = property?.GetCustomAttribute<DisplayNameAttribute>();

if (attribute != null)
{
Console.WriteLine(attribute.Name);
}

このコードでは、ユーザー名 が出力されます。

Attributeは、GetCustomAttributeGetCustomAttributes を使って取得できます。

11-5. Attributeを使うとパフォーマンスは悪くなりますか?

Attributeを付けること自体が大きなパフォーマンス低下につながることは通常ありません。

ただし、実行時にリフレクションでAttributeを大量に取得する処理は、パフォーマンスに影響する場合があります。

たとえば、毎回次のような処理を大量に行う場合は注意が必要です。

C#
var attributes = typeof(User).GetCustomAttributes(false);

頻繁にAttribute情報を使う場合は、一度取得した結果をキャッシュするのが効果的です。

C#
private static readonly Dictionary<Type, object[]> AttributeCache = new();

public static object[] GetAttributes(Type type)
{
if (!AttributeCache.TryGetValue(type, out var attributes))
{
attributes = type.GetCustomAttributes(false);
AttributeCache[type] = attributes;
}

return attributes;
}

つまり、Attributeそのものよりも、Attributeをどのように読み取るかが重要です。

まとめ

C# Attributeは、クラス、メソッド、プロパティなどに追加情報を付与するための仕組みです。

[Obsolete][Serializable][Required][HttpGet] など、C#や.NETの開発ではさまざまな場面でAttributeが使われます。

Attributeを使うことで、コードの意図を明確にしたり、フレームワークと連携したり、リフレクションを使って共通処理を自動化したりできます。

一方で、Attributeを付けるだけで必ず何かの処理が自動実行されるわけではありません。コンパイラ、フレームワーク、自分で実装した読み取り処理などがAttributeを参照して初めて意味を持ちます。

自作Attributeを作る場合は、System.Attribute を継承し、必要に応じて AttributeUsage で適用対象を指定します。必須の情報はコンストラクタ引数、任意の情報はプロパティとして設計すると分かりやすくなります。

また、リフレクションでAttributeを取得する場合は、パフォーマンスにも注意が必要です。大量に取得する場合や頻繁に実行される処理では、キャッシュを活用するとよいでしょう。

C# Attributeは、初心者にとって最初は少し分かりにくい機能ですが、仕組みを理解すると非常に便利です。標準Attributeの使い方から始めて、必要に応じて自作Attributeやリフレクション活用にも挑戦していくと、C#らしい柔軟で保守しやすいコードを書けるようになります。