C# の属性とは何か

C# には、クラスやメソッド、プロパティなどに追加情報を付与する「属性(Attribute)」という仕組みがある。属性はコードの動作そのものを変えるわけではなく、メタデータとしてアセンブリに埋め込まれ、コンパイラやフレームワーク、あるいはリフレクションを通じて実行時に参照される。

属性の基本構文

属性は角括弧 [] の中に記述し、対象の直前に置く。

[Obsolete("このメソッドは非推奨です")]
public void OldMethod()
{
    Console.WriteLine("古い処理");
}

この例では Obsolete 属性をメソッドに付与している。コンパイラはこのメソッドが呼ばれた箇所で警告を出すようになる。属性の正式名称は ObsoleteAttribute だが、末尾の Attribute は省略できる。

属性を付与できる対象

属性はメソッドだけでなく、さまざまな要素に付けられる。

クラス・構造体
メソッド
プロパティ
フィールド
列挙型
パラメータ
戻り値
アセンブリ全体

たとえば、クラスに属性を付ける場合は次のようになる。

[Serializable]
public class UserData
{
    public string Name { get; set; }
    public int Age { get; set; }
}

Serializable 属性を付けることで、このクラスのインスタンスがシリアライズ可能であることをランタイムに伝えている。

属性に引数を渡す

属性にはコンストラクタ引数と名前付き引数の 2 種類のパラメータを渡せる。

[Obsolete("代わりに NewMethod を使ってください", true)]
public void OldMethod()
{
    Console.WriteLine("古い処理");
}

1 つ目の引数はメッセージ(コンストラクタ引数)、2 つ目の true はコンパイルエラーにするかどうかのフラグだ。true を指定すると、このメソッドを呼び出したコードはコンパイルエラーになる。

名前付き引数は次のように書く。

[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern int MessageBox(IntPtr hWnd, string text, string caption, uint type);

SetLastErrorCharSet は名前付き引数で、順序を問わず指定できる。

複数の属性を付ける

1 つの対象に複数の属性を付けたい場合、個別に書く方法とカンマ区切りで書く方法がある。

個別に記述

属性ごとに角括弧で囲む。可読性が高く、属性が多い場合に見やすい。

カンマ区切り

1 つの角括弧内にカンマで並べる。行数を減らしたいときに便利。

// 個別に記述
[Serializable]
[Obsolete("非推奨です")]
public class OldData { }

// カンマ区切り
[Serializable, Obsolete("非推奨です")]
public class OldData2 { }

どちらも意味は同じだが、プロジェクトのコーディング規約に合わせて統一するのがよい。

属性が使われる場面

属性は .NET のあらゆる場所で活用されている。フレームワークの機能を制御したり、テストランナーがテストメソッドを発見したり、シリアライザがプロパティ名を変更したりと、用途は幅広い。

コンパイラへの指示

ObsoleteConditional のように、コンパイラの挙動を変える属性がある。警告やエラーの制御、条件付きコンパイルに使われる。

フレームワークとの連携

ASP.NET[HttpGet][Authorize]、Entity Framework の [Key][Required] など、フレームワークが属性を読み取って動作を決定する。

テストフレームワーク

NUnit の [Test] や xUnit の [Fact] のように、テストランナーが属性を手がかりにテストメソッドを検出・実行する。

属性はそれ自体が何かを実行するわけではない。あくまでメタデータとしてコードに情報を付加し、それを読み取る側(コンパイラ、フレームワーク、リフレクション)が意味を解釈して初めて効果を発揮する。この「宣言的に情報を付ける」というアプローチが、C# のコードを簡潔かつ柔軟に保つ鍵となっている。