ホームに戻る
出典 :
Attribute クラス (System) | Microsoft Learn チュートリアル: カスタム属性の定義と読み取り - C# | Microsoft Learn 属性 - C# によるプログラミング入門 | ++C++; // 未確認飛行 C
関連 :
[.NET]リフレクション レコード プライマリコンストラクタ ジェネリックプログラミング
目次 :

C#における属性( Attribute )

クラスやメンバなどに対して追加で付与する情報を「属性(カスタム属性)」と呼ぶ。 C++などの属性と共通の概念で、C#ではユーザが独自に定義した属性をクラスやメンバに対して付与することができる。 属性を用いることで以下のようなことが可能となる。
付与された属性を基にコンパイラがコードを自動生成するものも存在するため、活用することでコードが簡潔となり、保守性が高まる。

属性の使用(付与)

属性は[]で囲み、付与したい要素の直前に記述する。 画像 コードの記述次第では属性の付与先をコンパイラが判断できなくなるため、対象を[]内で指定する。 画像 以下は、属性をクラスに対して付与する場合の例である。 画像 また、複数の属性を指定する場合は,(カンマ)で区切るか、[]を列記する。 画像

付与対象の指定

属性を付与できる対象は以下のとおり。 「指定」列が「必須」となっている項目は、常に対象を明示する必要がある。
付与対象 指定
assembly アセンブリ 必須
module モジュール 必須
type
field フィールド
method メソッド
property プロパティ
event イベント
param メソッドの引数
return メソッドの戻り値 必須
記述例
[assembly: AssemblyTitle("Test Attribute")] //< アセンブリに付与 [Serializable] //< クラスに付与 public class SampleClass { [Obsolete("次期版で削除します。使わないでください。")] //< メソッドに付与 public void Test( [In, Out] ref int n ) //< 引数( n )に付与 { n *= 2; } }
メソッドの戻り値に属性を付与する場合はreturnで明示する必要がある。
// メソッドの戻り値に属性を付与したい // 以下、誤ったコード [DllImport("msvcrt.dll")] [MarshalAs(UnmanagedType.I4)] //< 対象が明示されていないため、メソッドに付与されてしまう public static extern int puts( [MarshalAs(UnmanagedType.LPStr)] string m); // 以下、上記の修正 [method: DllImport("msvcrt.dll")] //< メソッドに付与 ( method ) [return: MarshalAs(UnmanagedType.I4)] //< メソッドの戻り値に付与 ( return ) public static extern int puts( [param: MarshalAs(UnmanagedType.LPStr)] string m); //< メソッドの引数に付与 ( param )

プロパティとイベントへの付与

自動プロパティやイベントは、フィールドとメソッドが内部的に生成される。 このそれぞれに属性(ここでは X )を付与する場合は以下のように記述する。
using System; class XAttribute : Attribute { } class Sample { // プロパティへの属性付与 [property: X] //< プロパティ自体 public int Property { [method: X] //< get に対応するメソッド get => 0; [method: X] //< set に対応するメソッド [param: X] //< set が受け取る value 引数 set { } } [field: X] //< 自動生成フィールド(バッキングフィールド) public int _Property { get; } // イベントへの属性付与 [event: X] //< イベント自体 public event Action Event { [method: X] //< add に対応するメソッド [param: X] //< add が受け取る value 引数 add { } [method: X] //< remove に対応するメソッド [param: X] //< remove が受け取る value 引数 remove { } } [field: X] //< 自動生成フィールド(バッキングフィールド) public event Action _Event; }

プライマリコンストラクタへの付与

プライマリコンストラクタに属性を付与する際も、属性の付与先を明示する必要がある。 特にレコード型はプライマリコンストラクタの引数と同名のプロパティが自動生成されるが、これも付与先を選択できる。
class XAttribute : Attribute; // プライマリコンストラクタを用いるクラス [X] //< 対象 : クラス //< 省略しない場合は [type: X] [method: X] //< 対象 : プライマリコンストラクタ class C(); // プライマリコンストラクタを用いるレコード record R( [X] //< 対象 : プライマリコンストラクタの引数 //< 省略しない場合は [param: X] [property: X] //< 対象 : プロパティ [field: X] //< 対象 : バッキングフィールド int Property );

組み込み(定義済み)の属性

標準ライブラリで提供される定義済み属性の一部を抜粋。 属性(クラス)名はいずれも Attributeで終わっているが、C#からこれらを利用する場合、この Attributeは省略できる。 例 :ObsoleteAttribute[Obsolete("警告")]
影響先 属性名 名前空間 効果
コンパイラ ConditionalAttribute System.Diagnostics 指定された条件付きシンボルが定義されている場合のみ、 コンパイル対象に含める。 複数適用時はOR結合。 // CONDITION1 が定義されていれば Method1() が有効 [Conditional("CONDITION1")] public static void Method1(int x) { Console.WriteLine("CONDITION1 is defined"); } // CONDITION1 または CONDITION2 が定義されていれば Method2() が有効 [Conditional("CONDITION1"), Conditional("CONDITION2")] public static void Method2() { Console.WriteLine("CONDITION1 or CONDITION2 is defined"); }
AttributeUsageAttribute System 属性の用途を指定する。 ユーザ定義属性で使用する(後述)。
ObsoleteAttribute System 旧式化したコードであることを示す。 付与された対象を使用した場合、 コンパイラが警告を発する。 // OldMethod() を呼び出すと警告される [Obsolete("旧いメソッドです。")] public void OldMethod( ref int n ) { n *= 2; }
Visual Studio CategoryAttribute DefaultValueAttribute DescriptionAttribute BrowsableAttribute System.ComponentModel コンポーネントクラス(GUIコントロール)のプロパティに付与すると、 Visual Studioのプロパティエディタで値を編集できるようになる。 (詳細割愛)
中間言語(IL)
実行環境
(.NET)
DllImportAttribute System.Runtime.InteropServices ネイティブ(機械語翻訳済)のDLLからメソッドをインポートする。
ComImportAttribute System.Runtime.InteropServices アンマネージドなDLLからCOMクラスをインポートする。
アセンブリ AssemblyDescriptionAttribute System.Reflection アセンブリに説明文を付与する。 (ファイルプロパティ「コメント」から参照可能。)

ユーザ定義の属性

属性はSystem.Attributeを継承したクラスとして定義する。 ユーザ定義の属性名も末尾にAttributeを付与するのが通例であるが、組み込み属性同様、付与時には省略が可能である。
例 : Author 属性( AuthorAttribute )
using System; // Author (作者情報)属性の定義 [AttributeUsage( AttributeTargets.Class | AttributeTargets.Struct )] //< 適用対象の規定 public class AuthorAttribute : Attribute { // private フィールド private string Name; //< 作者名 // public フィールド public string Affiliation; //< 作者の所属 // コンストラクタ public AuthorAttribute(string name) { this.Name = name; } } // Author 属性の適用(#1) : 属性パラメータを指定 [Author("Andrei Hejilsberg")] class Test1 { : } // Author 属性の適用(#2) : 属性パラメータと名前付きパラメータを指定 [Author("Andrei Hejilsberg", Affiliation="Microsoft")] class Test2 { : }
上記はAuthor属性(AuthorAttributeクラス)の定義と付与を示している。 まずAttributeUsageAttributeTargets.ClassおよびAttributeTargets.Structを指定し、属性をクラスおよび構造体に適用可能としている。 Author属性には引数を1つとるコンストラクタが定義されており、付与時に属性パラメータはコンストラクタに渡される(#1)。 また、属性クラスにおけるpublicなフィールドおよびプロパティは、付与時に「名前付きパラメータ」で設定することができる(#2)。 (属性パラメータ、名前付きパラメータともに複数指定が可能。その場合は,(カンマ)で区切る。) 属性の使用方法を定めるAttributeUsageAttributeには以下のプロパティが定義されている。
このうち、ValidOnは属性パラメータとして、それ以外は名前付きパラメータとして指定する。 ここで、Author属性はクラスまたは構造体の作者を示すことを目的としている。 一つのクラスを複数人で開発することがあるならば、AllowMultiple = trueとする。 派生クラスの作者は基底クラスと同じとは限らない(作者情報を引き継げない)ため、Inherited = falseとする。 これらを踏まえてAuthor属性の定義を書き直すと以下のようになる。
using System; // Author 属性の定義 [AttributeUsage( AttributeTargets.Class | AttributeTargets.Struct //< 適用対象( ValidOn ) : クラスおよび構造体 AllowMultiple = true, //< 複数回適用 : 可 Inherited = false )] //< 属性値継承 : なし public class AuthorAttribute : Attribute { : }

属性情報の取得

Attributeクラスの静的メソッドにより、付与された属性の情報を取得することができる。
メソッド名 戻り値 解説
Attribute.GetCustomAttribute() Attribute 付与された属性への参照。該当する属性が無い場合はnull 対象に付与された属性(一つ)を取得する。複数の属性が付与されている場合は例外が発生する。
Attribute.GetCustomAttributes() Attribute[] 付与された属性への参照の配列。該当する属性が無い場合は空配列。 対象に付与された属性をすべて取得する。
いずれも戻り値はAttributeにラップされるため、元の型を得るにはキャストが必要。

コード
using System; using System.Reflection; // Attribute 属性の定義 [AttributeUsage( AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method, AllowMultiple = true, Inherited = false )] public class AuthorAttribute : Attribute { private string name; public AuthorAttribute(string name) { this.name = name; } public string Name { get { return this.name; } } } // クラス AuthorTest に Author 属性を付与(複数回) [Author("Stephanie McMahon")] [Author("Hunter Herst Helmsly")] class AuthorTest { // 各メソッドに Author 属性を付与 [Author("Kurt Angle")] public static void A() { } [Author("Rocky Mavia")] public static void B() { } [Author("Chris Jericho")] public static void C() { } } // テストプログラム class AttributeTest { static void Main() { GetAllAuthors( typeof(AuthorTest) ); } // クラスおよびクラス中のメソッドの作者情報を取得 static void GetAllAuthors( Type t ) { // クラスの型名を出力 Console.WriteLine( $@"type name: {t.Name}" ); // クラスに付与された作者情報を取得 GetAuthors( t ); // メソッドの作者情報を取得 foreach( MethodInfo info in t.GetMethods() ) { Console.WriteLine( $@" method name: {info.Name}" ); GetAuthors( info ); } } // メンバごとの作者情報を取得 static void GetAuthors( MemberInfo info ) { // Attribute.GetCustomAttributes() により付与された属性をすべて取得 Attribute[] authors = Attribute.GetCustomAttributes( info, typeof(AuthorAttribute) ); foreach( Attribute att in authors ) { AuthorAttribute author = att as AuthorAttribute; if( author != null ) { Console.WriteLine( $@" author name: {author.Name}" ); } } } }
実行結果
type name: AuthorTest author name: Hunter Herst Helmsly author name: Stephanie McMahon method name: GetHashCode method name: Equals method name: ToString method name: A author name: Kurt Angle method name: B author name: Rocky Mavia method name: C author name: Chris Jericho method name: GetType
解説
AuthorTestクラスおよびそのメソッドに付与されたAuthor属性を取得するテストプログラムである。 Attribute.GetCustomAttribute()メソッドは付与された属性が複数の場合は例外が投げられるため、 付与された属性が単一(または無し)であると判明している場合以外はAttribute.GetCustomAttributes()を用いるとよい。 Attribute.GetCustomAttributes()には対象のオブジェクト、および実際の属性の型(AuthorAttribute の型情報)を渡している。 取得した属性はAuthorAttributeにキャストし、作者名を出力している。

ジェネリック属性

C#11からは、型引数を取るジェネリクスとして属性を定義できるようになった。 (付与する時点で)既知の型を型引数としてとることができる。 (未知の型を型引数とすることはできない。 例えば以下のコードにおいて、MyClassが型引数を取るジェネリクスで、その型引数をTypeConverterAttributeに渡そうとするとエラーとなる。)
// ジェネリック属性 TypeConverter class TypeConverterAttribute<T> : Attribute { } // TypeConverter の付与(この時点で MyConverter は既知の型) [TypeConverter<MyConverter>] class MyClass { }