29.属性
CLI はプログラマに、
custom-attributes(カスタム属性)、またはもっと単純に
attribute(属性)と呼ばれる、新たな種類の宣言情報を作り上げることを可能とします。プログラマはそれから様々なプログラム・エンティティに属性をアタッチすることができ、実行時環境で属性情報を取得することができます。[注意:例えば、フレームワークはプログラム要素からドキュメントへのマッピングを提供する特定のプログラム要素(クラスや関数などの)を示すことができる HelpAttribute 属性を定義するかもしれません。]
属性は属性クラス(
§29.1 )の宣言を通して定義し、そして、配置パラメータと名前付けパラメータ(
§29.1.2 )を持つことができます。属性はC++プログラム中のエンティティに、属性指定(
§29.2)を使って付与され、実行時に属性インスタンス(
§29.3 )として取得されます。
メタデータの詳細は
§34.16 を参照のこと。
29.1 Attribute クラス
抽象refクラス System::Attribute から、直接的、非直接的にかかわらず、派生したクラスは、
attribute class(属性クラス)です。属性クラスの宣言は宣言に配置することができる新しい種類の属性を定義します。[注意:簡単のために、属性クラスは Attribute の後置子で名前付けをします。属性の使用ではこの後置子を含めても省略してもかまいません。]
ジェネリック・クラス宣言(
§31.1)は直接的、非直接的に基底クラスとして System::Attribute を使うべきではありません。
29.1.1 属性の使い方
System::AttributeUsageAttribute 属性(
§29.4.1 )は属性クラスがどのように使われうるのかを記述するために使われます。[注意:属性型の名前は後置子 Attribute で終わる時、属性としてその名前が使われ、その後置子を取り除いた名前を他の属性が持っていなければ、省略することができます。]
AttributeUsage は配置パラメータ(
§29.1.2 )を持ちます。その配置パラメータは、属性クラスが使うことができる宣言の種類を指定することを可能とします。[例:例、
[AttributeUsage(AttributeTargets::Class | AttributeTargets::Interface)]
public ref class SimpleAttribute : Attribute {};
は SimpleAttribute という名前の属性クラスを定義し、この属性は ref クラスとインターフェイス・クラスの定義にのみ配置することができます。例、
[Simple] ref class Class1 { ... };
[Simple] interface class Interface1 { ... };
は Simple 属性のいくつかの使い方を示しています。この属性は SimpleAttribute という名前で定義されているのですが、この属性を使う時には、Attribute 後置子は省略することができ、結果として短い名前 Simple となります。故に、上の例は以下のものと意味的に等価です。
[SimpleAttribute] ref class Class1 { ... };
[SimpleAttribute] interface class Interface1 { ... };
]
AttributeUsage は AllowMultiple と呼ばれる名前付きパラメータ(
§29.1.2 )を持ち、それはその属性が与えられたエンティティに一度より多く指定することができるかどうかを示しています。もし、属性クラスの AllowMultiple が真である場合、その時、そのクラスは
multi-use attribute class (重複使用属性クラス) であり、一つのエンティティに複数回指定することができます。もし、属性クラスの AllowMultiple が偽であるか、指定されていなければ、その時、そのクラスは
single-use attribute class (単回使用属性クラス) であり、エンティティに一回より多く指定するべきではありません。
[例:例
[AttributeUsage(AttributeTargets::Class, AllowMultiple = true)]
public ref class AutorAttribute : Attribute {
String^ name;
public:
AuthorAttribute(String^ name) : name(name) { }
property String^ Name { String^ get() { return name; } }
};
は AuthorAttribute という名前の重複使用属性クラスを定義します。例、
[Author("Brian Kernighan"), Author("Dennis Ritchie")]
ref class Class1 { ... };
は Author 属性の二回の使用をクラス定義に示しています。]
AttributeUsage はもう一つ Inherited という名前の名前付きパラメータ(
§29.1.2)を持っており、それはその属性が基底クラスで指定された時、その基底クラスから派生したクラスによっても継承されるかどうかを示しています。もし、ある属性クラスの Inherited が真である場合、その時はその属性は継承されます。もし、属性クラスの Inherited が偽である場合、その属性は継承されません。もし、指定されていなければ、デフォルト値は真です。
AttributeUsage 属性を持っていない属性クラス R にそれを当てはめると、
ref class R : Attribute { ... };
は、次と等価です。
[AttributeUsage(AttributeTargets::All, AllowMultiple = false)]
ref class R : Attribute { ... };
29.1.2 配置(Positional)名前付き(named)パラメータ
属性クラスは
positinal parameter (配置パラメータ) と
named parameter (名前付きパラメータ) を持つことができます。
各々の属性クラスのための public インスタンス・コンストラクタはその属性クラスで配置パラメータの妥当な順序を定義します。
属性クラスの各々の非静的 public リード・ライト・フィールドとプロパティは属性クラスのために名前付きパラメータを定義します。
プロパティのアクセサの双方はプロパティに定義される名前付きパラメータは public でなければなりません。
[例:例、
[AttributeUsage(AttributeTargets::Class)]
public ref class HelpAttribute : Attribute {
public:
HelpAttribute(String^ url) { // url は配置パラメータ
...
}
property String^ Topic { // Topic は名前付けパラメータ
String^ get() { ... };
void set(String^ value) { ... };
}
property String^ Url { String^ get() { ... } }
};
は一つの配置パラメータ (String^ Url) と一つの名前付きパラメータ (String^ Topic) を持つ、 HelpAttribute という名前の属性クラスを定義します。それは非静的、そして、public だけれど、プロパティ Url は読み書きではないので名前付きパラメータを定義しません。
この属性クラスは次のように使われるでしょう:
[Help("http://www.mycompany.com/.../class1.htm")]
ref class Class1 {
};
[Help("http://www.mycompany.com/.../Misc.htm", Topic = "Class2")]
ref class Class2 {
};
]
型パラメータ(
§31.1.1)でも開いた生成型(
§31.2.1)のどちらもカスタム属性のコンストラクタの引数になるべきではありません。
29.1.3 属性パラメータ型
attribute parameter type(属性パラメータ型)は属性クラスの位置づけパラメータと名前付きパラメータの型です。
これらの型は以下のどれかであるべきです。
- 次の型の一つ:Boolean, System::Byte, System::SByte, System::Char, System::Int16, System::Int32, System::Int64, System::Single, System::Double ないし、これらの型の一つに対応するネイティブ型。
- System::String^ 型。
- System::Object^ 型。
- System::Type^ 型。
- そのアクセス可能性が public であり、そのネストしている(任意の)型もまた public のアクセス可能性を持っている列挙型。
- 上述の型の一次元 ::cli::array
29.2 Attribute 仕様
Attribute specification (属性仕様) は宣言に先んじて定義される属性の適用です。
属性は宣言に指定された追加宣言情報の一欠片です。
属性は(アセンブリに含む属性を指定することで)ファイル・スコープで、
accessor-declaration (アクセサ宣言)(
§19.5.3 )、
class-specifier(
§19.1 )、
delegate-specifier(
§27.1 )、
elaborated-type-specifier、
enum-specifier(
§26.1 )、
enumerator's
identifier、
event-definition(
§19.6 )、
function-definition、
generic-parameter(
§31.1.1 )、
member-declaration(
§19.1 )、
parameter-array(
§18.4 )、
parameter-declaration、
property-definition(
§19.5 )、そして、
simple-declarationに指定することができます。
属性は
attribute section (属性区分)で指定されます。
属性区分は角括弧の組からなり、一つ以上の属性のコンマ区切りリストに囲まれます。
そのリストに指定されている属性の順、そして、配置されている同じプログラム・エンティティにアタッチする区分の順には意味はありません。
例えば、属性指定 [A][B]、[B][A]、[A, B]、[B, A] は等価です。
attributes :
attribute-sections
attribute-sections :
attribute-sectionsopt attribute-section
attribute-section :
[ attribute-target-specifieropt attribute-list ]
attribute-target-specifier :
attribute-target :
attribute-target :
assembly
class
constructor
delegate
enum
event
field
interface
method
parameter
property
returnvalue
struct
attribute-list :
attribute
attribute-list , attribute
attribute :
attribute-name attribute-argumentsopt
attribute-name :
type-name
attribute-arguments :
( positional-argument-listopt )
( positional-argument-list , named-argument-list )
( named-argument-list )
positional-argument-list :
positional-argument
positional-argument-list , positional-argument
positional-argument :
attribute-argument-expression
named-argument-list :
named-argument
named-argument-list , named-argument
named-argument :
identifier = attribute-argument-expression
attribute-argument-expression :
assignment-expression
属性は
attribute-name(属性名称) とオプションとなる配置、名前付き引数のリストからなります。
配置引数は(すくなくても)名前付き引数に先行します。
配置引数は
attribute-argument-expression で構成されます。
名前付き引数は名前と等号記号に続いて
attribute-argument-expression で構成され、一緒に、単純な代入と同じルールで束縛されます。
名前付け引数の順番に意味はありません。
[注意:CLI において、関数はメソッドと呼ばれるので、関数のターゲット指定子は method です。]
attribute-name 識別子は属性クラスです。
type-name は属性クラスを指すべきです。[例:例、
ref class Class1 {};
[Class1] ref class Class2 {}; // エラー
は Class1 が属性クラスではない時、属性クラスとして Class1 を使うことを仮定しているので、不正なプログラムとなります。]
標準化
attribute-target 名称は assembly, class constructor, delegate, enum, event, field, interface, method, parameter, property, returnvalue、そして、struct です。これらのターゲット名称は以下のようなコンテキスト中でのみ使われるべきです。
- assembly -- アセンブリ。その場合、attribute-section はセミコロンが付き従う。[例:[assembly:CLSCompliant(true)]; ]
- class -- ref クラス
- constructor -- コンストラクタ
- delegate -- デリゲート
- enum -- 列挙(ネイティブやCLI)
- event -- イベント
- field -- フィールド。トリビアル・イベントやトリビアル・プロパティもこのターゲットで属性を持つことができる
- interface -- インターフェイス・クラス
- method -- デストラクタ、ファイナライザ、関数、演算子、プロパティの get と set アクセサ、そして、イベントの add と remove アクセサ。トリビアル・イベントやトリビアル・プロパティもまたこのターゲットで属性を持つことができる
- parameter -- コンストラクタ、関数、演算子、ないし、プロパティやイベントのアクセサ中のパラメータ
- property -- プロパティ
- returnvalue -- デリゲート、メソッド、演算子、そして、プロパティの get アクセサ
- struct -- 値クラス
属性がファイル・スコープに位置する時、assembly の
attribute-target が要求されます。
あるコンテキストは一つより多い属性の指定を許します。プログラムは明示的に
attribute-target-specifier(属性ターゲット指定子) を含むことによってターゲットを指定することができます。他の全ての配置において、意味あるデフォルトが与えられ、しかし、
attribute-target-specifier(属性ターゲット指定子) は確かに曖昧な場合にデフォルトを上書きするか明示するために(ないし、曖昧さのない場合でのデフォルトを明示するために)使われることができます。故に、典型的に
attribute-target-specifier(属性ターゲット指定子) は省略されることができます。
潜在的に曖昧なコンテキストは以下のように解決されます。
- デリゲート宣言で指定された属性は宣言されたデリゲートとその返値のどちらにも適用できます。attribute-target-specifier(属性ターゲット指定子) が不在の場合、属性はデリゲートに適用されます。
delegate attribute-target-specifier(属性ターゲット指定子) の存在はその属性がそのデリゲートにかけられていることを示しており、retuenvalue attribute-target-specifier(属性ターゲット指定子)の存在はその属性が返値に適用されることを示します。
- 関数宣言に指定する属性は宣言された関数かその返値かのどちらにも適用できます。attribute-target-specifier(属性ターゲット指定子) が不在の場合、属性は関数に適用されます。method attribute-target-specifier(属性ターゲット指定子) の存在はその属性が関数に適用されることを示しています。retuenvalue attribute-target-specifier(属性ターゲット指定子) の存在はその属性が返値に適用されることを示しています。
- 演算子宣言に指定された属性は宣言された演算子かその返値のどちらにも適用できます。attribute-target-specifier(属性ターゲット指定子) が不在の場合、属性は演算子に適用されます。method attribute-target-specifier(属性ターゲット指定子) の存在はその属性が演算子に適用されることを示しています。
retuenvalue attribute-target-specifier(属性ターゲット指定子) の存在はその属性が返値に適用されることを示しています。
- トリビアル・プロパティ宣言に指定された属性は、宣言されたプロパティか、それに関連するフィールド(もし、プロパティが abstract でなければ)、ないし、関連したセットと get アクセサ関数に適用することができます。attribute-target-specifier(属性ターゲット指定子) が不在の場合、属性はプロパティ宣言に適用されます。property attribute-target-specifier(属性ターゲット指定子) の存在はその属性がプロパティに適用されることを示しています。field attribute-target-specifier(属性ターゲット指定子) の存在はその属性がフィールドに適用されることを示しています。そして、method attribute-target-specifier(属性ターゲット指定子) の存在はその属性がアクセサ関数に適用されることを示しています。
- トリビアル・イベント宣言に指定された属性は、宣言されたイベント、関連するフィールド(もし、そのイベントが abstract でなければ)、ないし、関連した追加と削除関数に適用することができます。attribute-target-specifier(属性ターゲット指定子) が不在の場合、属性はイベント宣言に適用されます。event attribute-target-specifier(属性ターゲット指定子) の存在はその属性がイベントに適用されることを示しています。field attribute-target-specifier(属性ターゲット指定子) の存在はその属性はフィールドに適用されることを示しています。そして、method attribute-target-specifier(属性ターゲット指定子) の存在はその属性が関数に適用されることを示しています。
実装は他の属性ターゲット指定子を受け入れることができ、属性ターゲット指定子の目的は未規定です。しかしながら、その様なターゲットを認識しない実装は診断を発行するべきです。
簡単に、属性クラスは Attribute の後置子で名前付けされています。
attribute-name(属性名称) はこの後置子を含んだり省略したりすることができます。後置子が省略されている属性参照を解決する目的の時、もし、後置子が付いているものとついていないものの双方が見つかったら、曖昧さが示され、そのプログラムは不正です。[例:例、
[AttributeUsage(AttributeTargets::All)]
public ref class X : Attribute {};
[AttributeUsage(AttributeTargets::All)]
public ref class XAttribute : Attribute {};
[X] // エラー:あいまいです
ref class Class1 {};
[XAttribute] // XAttribute を参照する
ref class Class2 {};
は X と XAttribute という名前の二つの属性クラスを示しています。属性参照 [X] はあいまいです。そのため、それは X ないし、XAttribute を参照可能となります。属性参照 [XAttribute] は曖昧ではありません。(ですが、もしも XAttributeAttribute という名前の属性クラスがあるかもしれません!)もしも、クラス X の宣言が削除されたら、その時、双方の属性は以下のように XAttribute と名前付けされた属性クラスを参照します。
[AttributeUsage(AttributeTargets::All)]
public ref class XAttribute : Attribute {};
[X] // XAttribute を参照する
ref class Class1 {};
[XAttribute] // XAttribute を参照する
ref class Class2 {};
]
もしも、単一使用クラスが同じエンティティに一度より多く使われているプログラムは不正です。[例:例
[AttributeUsage(AttributeTargets::Class)]
public ref class HelpStringAttribute : Attribute {
String^ value;
public:
HelpStringAttribute(String^ value) {
this->value = value;
}
property String^ Value { String^ get() { ... } }
};
[HelpString("Description of Class1"]
[HelpString("Anotherdescription of Class1")] // エラー
public ref class Class1 {};
は、単一使用な属性クラスである HelpStringを、Class1 の宣言に一度より多く使おうと試みているので、プログラムは不正であるという結果となります。]
演算 E は、もし以下の全ての条件が真となれば、
attribute-argument-expression(属性引数演算) です。
- E の型は属性パラメータ型(§29.1.3 )である。
- コンパイル時、E の値が次のどれか一つで解決できる。
- 定数値
- System::Type^ object
- atteibute-argument-expression(属性引数演算) の一次元 ::cli::array
[例:
[AttributeUsage(AttributeTargets::Class)]
public ref class MyAttribute : Attribute {
public:
property int P1 {
int get() { ... }
void set(int value) { ... }
}
property type^ P2 {
Type^ get() { ... }
void set(Type^ value) { ... }
}
property Object^ P3 {
Object^ get() { ... }
void set(Object^ value) { ... }
}
};
[My(P1 = 1234, P3 = gcnew array<int>{1, 3, 5}, P2 = float::typeid)]
ref class MyClass {};
]
型や関数に与えられる属性の組はその型や関数の定義において指定されるべきです。定義でもない型や関数の宣言は同じ属性セットか属性無しかを持つべきです。[例:与えられた二つの属性 XAttribute と YAttribute はクラスと関数とに適用することができます。
ref class R; // ok、リスト無し
[X]ref class R; // エラー、部分リスト
[Y]ref class R; // エラー、部分リスト
[X][Y]ref class R; // ok、全体リスト
[X][Y]ref class R { // 定義、全体リスト
[X] void F(); // エラー、部分リスト
};
[X][Y] void R::F() {} // 定義、全体リスト
]
29.3 Attribute インスタンス
attribute instance(属性インスタンス) は実行時に属性を表現するインスタンスです。
属性は属性クラス、配置引数、そして、名前付き引数で定義されます。属性インスタンスは配置と名前付き引数で初期化された属性クラスのインスタンスです。
属性インスタンスの取得は以下の節に記述されているようにコンパイル時と実行時処理の双方に含まれます。
29.3.1 属性のコンパイル
属性クラス T、
positional-argument-list (配置引数リスト) P、そして、
named-argument-list N を持つ
attribute (属性)のコンパイルは以下のステップから成り立っています。
- gcnew T(P) 形式の new-expression (new 演算) をコンパイルするためのコンパイル処理ステップに従う。
これらのステップはプログラムが不正であるか、実行時に含むことができる T のインスタンス・コンストラクタを決定するかの結果となる。
ここでこのインスタンス・コンストラクタを C と呼ぶ。
- もし、C が public アクセス可能性を持っていなければ、その時、プログラムは不正である。
- N 中の named-argument (名前付け引数) Arg ごとに、
- named-argument (名前付け引数) Arg の識別子を名前とする。
- 名前は T の非静的読み書き可能な public フィールドかプロパティと識別するべきである。もし、T はその様なフィールドやプロパティを持たなければ、その時、プログラムは不正である。
- 属性の実行し生成に以下の情報を保持する。
属性クラス T、T のインスタンス・コンストラクタ C、positional-argument-list (配置引数リスト) P と named-argument-list (名前付き引数リスト) N。
29.3.2 Attribute インスタンスの実行時取得
これは CLI 標準で決定されています。
29.4 予約済み属性
以下の属性は言語に影響を及ぼします。はじめに:
- System::AttributeUsageAttribute(§29.4.1 )は属性クラスがどのように使うことができるかを記述するために使われます。
- System::ObsoluteAttribute(§29.4.2 )はメンバーがもはや利用されないことを示すのに使われます。
- System::Security::Permissions::SecurityAttribute とそれから派生した属性(§29.4.4 )はCLI標準に記述のセキュリティ特性を呼び出すのに使われます。
29.4.1 AttributeUsage属性
属性 System::AttributeUsage はその属性クラスが使われる時、それがプログラム要素に一つより多く適用されることができるかどうか、そして、その属性が適用されたクラスから継承されたくらすによって継承されているかどうかを含んだ、振る舞いを記述するのに使われます。
AttributeUsage 属性で装飾された ref クラスは System::Attribute から直接的、非直接的に派生するべきです。さもなければ、プログラムは不正です。
AttributeUsageAttribute クラスのコンストラクタは System::AttributeTargets 型の引数を取ります。この列挙型は列挙子定義の数字を持ちます。そのいくつかは後にさらなる説明が必要です。
- Class は ref クラスに適用されうる属性を示す。
- Enum はネイティブ、ないし、CLI 列挙に適用されうる属性を示す。
- Field は CLI クラス型のデータ・メンバに適用されうる属性を示す。
- Interface はインターフェイス・クラスに適用されうる属性を示す。
- Method は CLI クラス型の関数に適用されうる属性を示す。
- Struct は値クラス適用されうる属性を示す。
[注意:この属性を使う例については、
§29.1.1 を参照のこと。]
この型のさらなる情報は、CLI 標準のパーティションIV を参照のこと。
29.4.2 Obsolete属性
属性 Obsolete はもはや使われない型と型のメンバをマークするのに使われます。
もし、プログラムは Obsolete 属性で修飾された型やメンバが使われると、その時、コンパイラは開発者に警告するために診断を発行するべきです。それで、与えられたコードは固定されることができます。特別に、もし、エラーとなるパラメータ(二番目のパラメータ)が与えられておらず、もし、エラーとなるパラメータが与えられ、その値が false を持っていれば、コンパイラは診断を発行するべきです。もし、エラー・パラメータが指定され、その値が true であれば、そのプログラムは不正です。
[例:例中で、
[Obsolute("このクラスは使用されません。代わりにクラス B を使ってください。", true)]
ref struct A {
void F() { }
};
ref struct B {
void F() { }
};
int main() {
A^ a = gcnew A(); // 診断
a->F();
}
クラス A は Obsolete 属性で修飾されています。main 中での A のそれぞれの使用は診断中に指定されたメッセージを含む結果となります。"このクラスは使用されません。代わりにクラス B を使ってください。"]
この型のより詳しい情報は、CLI 標準のパーティションIV を参照のこと。
29.4.3 Conditional 属性
CLI 標準は Conditional 属性を定義します。この属性は CLI の標的となる言語に
conditional method (条件メソッド) と
conditional attribute class (条件属性クラス) の定義を可能とする能力を提供することを許します。
C++/CLI はこの能力を提供することは
ありません。
だけれども、この型の属性は受け入れられ、それらはコード生成や実行に何の影響も持ちません。
29.4.4 セキュリティ属性
セキュリティ属性は System::Security::Permissions::SecurityAttribute から派生し、型と関数とアセンブリにのみ適用されます。セキュリティ属性の全てのコンストラクタは第一パラメータとして System::Permissions::SecurityAction(CLI 標準の §22.11 を参照)を取るべきです。
セキュリティ属性は属性コンストラクタの第一パラメータである SecurityAction によってアセンブリ、型、ないし、関数の仕様に追加構文を関連づけます。
セキュリティ属性の意味は実行エンジンによって提供されます。コンパイラの最適化はこれらの意味を保持するべきです。例えば、もしコンパイラがセキュリティ属性を持つ関数をインライン化した場合、コンパイラは関数呼び出しによって引き起こされるアクションとインライン化された関数の場所は等価であると保証するべきです。
29.5 相互互換(interoperation)のための属性
29.5.1 他のCLI実装言語との相互互換
29.5.1.1 DefaultMember属性
属性 System::Reflection::DefaultMemberAttribute はデフォルト・インデックス・プロパティに背景名を提供するために使われます。その属性はクラスに配置され、デフォルト・インデックス・プロパティの全ての多重定義は同じ名前を共有します。
29.5.1.2 MethodImplOption属性
この属性は
§19.6 ,
§19.6.2 、そして、
§34.7.4.5 で議論されています。
29.5.2 ネイティブ・コードとの相互互換
§18.5 の属性型 DllImport の議論を参照のこと。