ホームに戻る
出典 :
関連 :
目次 :
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
クラス)の定義と付与を示している。
まずAttributeUsage
にAttributeTargets.Class
およびAttributeTargets.Struct
を指定し、属性をクラスおよび構造体に適用可能としている。
Author
属性には引数を1つとるコンストラクタが定義されており、付与時に属性パラメータはコンストラクタに渡される(#1)。
また、属性クラスにおけるpublic
なフィールドおよびプロパティは、付与時に「名前付きパラメータ」で設定することができる(#2)。
(属性パラメータ、名前付きパラメータともに複数指定が可能。その場合は,
(カンマ)で区切る。)
属性の使用方法を定めるAttributeUsageAttribute
には以下のプロパティが定義されている。
ValidOn
: 属性の適用対象 (System.AttributeTargets
(Enum
))
AllowMultiple
: 同じ属性を同じ対象に複数回適用できるか (bool
)
Inherited
: 属性が適用されたクラスを継承したとき、属性値も併せて継承するか (bool
)
このうち、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 { }