22.1 全般
C# 言語の多くを使用すると、プログラマーはプログラムで定義されているエンティティに関する宣言型情報を指定できます。 たとえば、クラス内のメソッドのアクセシビリティは、method_modifierpublic
、protected
、internal
および private
で修飾することによって指定されます。
C# を使用すると、プログラマーは attributes と呼ばれる新しい種類の宣言型情報を自作できます。 プログラマーは、さまざまなプログラム エンティティに属性をアタッチし、実行時の環境で属性情報を取得できます。
注: たとえば、フレームワークでは、特定のプログラム要素 (クラスやメソッドなど) に配置できる
HelpAttribute
属性を定義すると、それらのプログラム要素からドキュメントへのマッピングを提供できます。 注釈
属性は、位置パラメーターと名前付きパラメーター (§22.2.3) を持つ属性クラス (§22.2) の宣言によって定義されます。 属性は、属性仕様 (§22.3) を使用して C# プログラムのエンティティにアタッチされ、実行時に属性インスタンスとして取得できます (§22.4)。
22.2 属性クラス
22.2.1 全般
抽象クラス System.Attribute
から派生するクラスは、直接的または間接的に関係なく、属性クラスです。 属性クラスの宣言は、プログラム エンティティに配置できる新しい種類の属性を定義します。 慣例により、属性クラスには Attribute
のサフィックスを付けた名前が付けられます。 属性の使用には、このサフィックスを含められますが、省略もできます。
ジェネリック クラス宣言では、直接または間接基底クラスとして System.Attribute
を使用しないでください。
例:
public class B : Attribute {} public class C<T> : B {} // Error – generic cannot be an attribute
終了サンプル
22.2.2 属性の使用
属性 AttributeUsage
(§22.5.2) は、属性クラスの使用方法を記述するために使用されます。
AttributeUsage
には位置パラメーター (§22.2.3) があり、属性クラスで使用できるプログラム エンティティの種類を指定できます。
例: 次の例では、
SimpleAttribute
と interface_declaration のみに配置できる という名前の属性クラスを定義し、Simple
属性の使用方法について説明します。[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface)] public class SimpleAttribute : Attribute { ... } [Simple] class Class1 {...} [Simple] interface Interface1 {...}
この属性は名前
SimpleAttribute
で定義されていますが、この属性を使用する場合は、Attribute
サフィックスが省略され、短い名前Simple
になります。 したがって、上記の例は、意味的に次と同等です。[SimpleAttribute] class Class1 {...} [SimpleAttribute] interface Interface1 {...}
終了サンプル
AttributeUsage
には、 という名前付きパラメーター (AllowMultiple
) があり、これは、指定のエンティティに対して属性を複数回指定できるかどうかを示します。 属性クラスの AllowMultiple
が True の場合、その属性クラスは、multi-use 属性クラスであり、エンティティで複数回指定できます。 属性クラスの AllowMultiple
が False または指定されていない場合、その属性クラスは single-use 属性クラスであり、エンティティに対して最大で 1 回指定できます。
例: 次の例では、
AuthorAttribute
という名前の multi-use 属性クラスを定義し、Author
属性の 2 つの使用方法を持つクラス宣言を示します。[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] public class AuthorAttribute : Attribute { public string Name { get; } public AuthorAttribute(string name) => Name = name; } [Author("Brian Kernighan"), Author("Dennis Ritchie")] class Class1 { ... }
終了サンプル
AttributeUsage
には、 という別の名前付きパラメーター (Inherited
) があり、これは、基底クラスで指定された属性がその基底クラスから派生したクラスによって継承されるかどうかを示します。 属性クラスの Inherited
が True の場合、その属性は継承されます。 属性クラスの Inherited
が Frue の場合、その属性は継承されません。 指定されていない場合、既定値は True です。
次のように、属性クラス X
に AttributeUsage
属性がアタッチされていない
class X : Attribute { ... }
これは、次と同じです。
[AttributeUsage(
AttributeTargets.All,
AllowMultiple = false,
Inherited = true)
]
class X : Attribute { ... }
22.2.3 位置パラメーターと名前付きパラメーター
属性クラスでは、位置パラメーターおよび名前付きパラメーターを使用できます。 属性クラスの各パブリック インスタンス コンストラクターは、その属性クラスの位置パラメーターの有効なシーケンスを定義します。 属性クラスの静的でないパブリック読み取り/書き込みフィールドとプロパティはそれぞれ、属性クラスの名前付きパラメーターを定義します。 プロパティで名前付きパラメーターを定義する場合、そのプロパティにはパブリック get アクセサーとパブリック set アクセサーの両方が含まれます。
例: 次の例では、1 つの位置パラメーター、
HelpAttribute
、および 1 つの名前付きパラメーター、url
を持つTopic
という名前の属性クラスを定義します。 非静的でパブリックであっても、プロパティUrl
は、読み取り/書き込みではないため、名前付きパラメーターを定義しません。 この属性の 2 つの使用方法も示されています。[AttributeUsage(AttributeTargets.Class)] public class HelpAttribute : Attribute { public HelpAttribute(string url) // url is a positional parameter { ... } // Topic is a named parameter public string Topic { get; set; } public string Url { get; } } [Help("http://www.mycompany.com/xxx/Class1.htm")] class Class1 { } [Help("http://www.mycompany.com/xxx/Misc.htm", Topic ="Class2")] class Class2 { }
終了サンプル
22.2.4 属性パラメーターの型
属性クラスの位置パラメーターと名前付きパラメーターの型は、属性パラメーター型に制限されます。これは次のとおりです。
- 次のいずれかの種類:
bool
、byte
、char
、double
、float
、int
、long
、sbyte
、short
、string
、uint
、ulong
、ushort
。 - 型
object
。 - 型
System.Type
。 - 列挙型。
- 上記の型の 1 次元配列。
- これらの型のいずれかがないコンストラクター引数またはパブリック フィールドは、属性指定の位置パラメーターまたは名前付きパラメーターとして使用しません。
22.3 属性の指定
属性の指定は、以前に定義された属性をプログラム エンティティに適用することです。 属性は、プログラム エンティティに対して指定される追加の宣言情報の一部です。 属性は、グローバル スコープで指定できます (包含アセンブリまたはモジュールの属性を指定するため)。また、type_declaration (§14.7)、class_member_declaration (§15.3)、interface_member_declaration (§18.4)、struct_member_declarations (§16.3)、enum_member_declaration (§19.2)、accessor_declaration (§15.7.3)、event_accessor_declaration (§15.8)、parameter_list の要素 (§15.6.2)、および type_parameter_list の要素 (§15.2.3) に対して指定できます。
属性は属性セクションで指定します。 属性セクションは、1 つ以上の属性のコンマ区切りのリストを囲む角かっこのペアで構成されます。 このようなリストで属性が指定される順序と、同じプログラム エンティティにアタッチされているセクションが配置される順序は重要ではありません。 たとえば、属性の仕様 [A][B]
、[B][A]
、[A, B]
、および [B, A]
は同等です。
global_attributes
: global_attribute_section+
;
global_attribute_section
: '[' global_attribute_target_specifier attribute_list ']'
;
global_attribute_target_specifier
: global_attribute_target ':'
;
global_attribute_target
: identifier
;
attributes
: attribute_section+
;
attribute_section
: '[' attribute_target_specifier? attribute_list ']'
;
attribute_target_specifier
: attribute_target ':'
;
attribute_target
: identifier
| keyword
;
attribute_list
: attribute (',' attribute)* ','?
;
attribute
: attribute_name attribute_arguments?
;
attribute_name
: type_name
;
attribute_arguments
: '(' ')'
| '(' positional_argument_list (',' named_argument_list)? ')'
| '(' named_argument_list ')'
;
positional_argument_list
: positional_argument (',' positional_argument)*
;
positional_argument
: argument_name? attribute_argument_expression
;
named_argument_list
: named_argument (',' named_argument)*
;
named_argument
: identifier '=' attribute_argument_expression
;
attribute_argument_expression
: non_assignment_expression
;
実稼働 global_attribute_target の場合、次のテキストでは、identifier に、assembly
または module
と等しいスペルが存在する必要があり、ここでの「等しい」もの、§6.4.3 で定義されています。 実稼働 attribute_targetの場合、次のテキストでは、上記と等値の定義を使用して、identifier に、assembly
または module
と等しくないスペルが存在する必要があります。
属性は attribute_name と、位置および名前付き引数の省略可能なリストで構成されます。 位置指定引数 (ある場合) は名前付き引数の前にあります。 位置引数は attribute_argument_expressionで構成されます。名前付き引数は、名前、等号、 attribute_argument_expressionで構成され、単純な代入と同じ規則によって制約されます。 名前付き引数の順序は重要ではありません。
注: 便宜上、array_initializer (§17.7) で使用できるのと同様に、global_attribute_section および attribute_section でもコンマを末尾で使用できます。 注釈
attribute_name は属性クラスを識別します。
属性をグローバル レベルで配置する場合は、global_attribute_target_specifier が必要です。 global_attribute_target が次と等しい場合:
-
assembly
— ターゲットは包含アセンブリです -
module
— ターゲットは包含モジュールです
global_attribute_target の他の値は使用できません。
標準化された attribute_target 名は、event
、field
、method
、param
、property
、return
、type
、および typevar
です。 これらのターゲット名は、次のコンテキストでのみ使用されます。
-
event
— イベントです。 -
field
— フィールドです。 フィールドに似たイベント (アクセサーのないイベント) (§15.8.2) と自動的に実装されるプロパティ (§15.7.4) も、このターゲットがある属性を持つことができます。 -
method
— コンストラクター、ファイナライザー、メソッド、演算子、プロパティの get アクセサーと set アクセサー、インデクサーの get アクセサーと set アクセサー、およびイベントの add および remove アクセサー。 フィールドに似たイベント (アクセサーのないイベント) も、このターゲットがある属性を持つことができます。 -
param
— プロパティの set アクセサー、インデクサーの set アクセサー、イベントの add および remove アクセサー、およびコンストラクター、メソッド、および演算子のパラメーター。 -
property
— プロパティとインデクサー。 -
return
— デリゲート、メソッド、演算子、プロパティの get アクセサー、およびインデクサーの get アクセサー。 -
type
— デリゲート、クラス、構造体、列挙型、およびインターフェイス。 -
typevar
— type パラメーター。
特定のコンテキストでは、複数のターゲットに対する属性の指定が許可されます。 プログラムは、attribute_target_specifier を含めることで、ターゲットを明示的に指定できます。 attribute_target_specifier がない場合は、既定値が適用されますが、attribute_target_specifier を使用すると既定値を確認またはオーバーライドできます。 コンテキストは次のように解決されます。
- デリゲート宣言の属性の場合、既定のターゲットはデリゲートです。 それ以外の場合で attribute_target が次と等しい場合:
-
type
— ターゲットはデリゲートです -
return
— ターゲットは戻り値です
-
- メソッド宣言の属性の場合、既定のターゲットはメソッドです。 それ以外の場合で attribute_target が次と等しい場合:
-
method
— ターゲットはメソッドです -
return
— ターゲットは戻り値です
-
- 演算子宣言の属性の場合、既定のターゲットは演算子です。 それ以外の場合で attribute_target が次と等しい場合:
-
method
— ターゲットは演算子です。 -
return
— ターゲットは戻り値です
-
- プロパティ宣言またはインデクサー宣言の get アクセサー宣言の属性の場合、既定のターゲットは関連付けられたメソッドです。 それ以外の場合で attribute_target が次と等しい場合:
-
method
— ターゲットは関連付けられたメソッドです。 -
return
— ターゲットは戻り値です
-
- プロパティ宣言またはインデクサー宣言の set アクセサーで指定された属性の場合、既定のターゲットは関連付けられたメソッドです。 それ以外の場合で attribute_target が次と等しい場合:
-
method
— ターゲットは関連付けられたメソッドです。 -
param
— ターゲットは、孤立した暗黙的パラメーターです。
-
- 自動的に実装されるプロパティ宣言の属性の場合、既定のターゲットはプロパティです。 それ以外の場合で attribute_target が次と等しい場合:
-
field
— ターゲットは、プロパティのコンパイラーによって生成されたバッキング フィールドです
-
-
event_accessor_declarations を省略したイベント宣言で指定された属性の場合、既定のターゲットは、イベント宣言です。 それ以外の場合で attribute_target が次と等しい場合:
-
event
— ターゲットはイベント宣言です -
field
— ターゲットはフィールドです。 -
method
— ターゲットはメソッドです。
-
-
event_accessor_declarations を省略していないイベント宣言の場合、既定のターゲットは、メソッドです。
-
method
— ターゲットは関連付けられたメソッドです。 -
param
— ターゲットは、孤立したパラメーターです。
-
他のすべてのコンテキストでは、attribute_target_specifier を含めることはできますが、含める必要はありません。
例: クラス宣言には、指定子
type
を含めるめることができますが、省略できます。[type: Author("Brian Kernighan")] class Class1 {} [Author("Dennis Ritchie")] class Class2 {}
サンプルの終了。
実装は他の attribute_targetを受け入れることができ、その目的は実装が定義されています。 このような attribute_target を認識しない実装では、警告が表示され、含まれている attribute_section を無視します。
慣例により、属性クラスには Attribute
のサフィックスを付けた名前が付けられます。
attribute_name には、このサフィックスを含めるめることができますが、省略できます。 具体的には、attribute_name は次のように解決されます。
-
attribute_name の右端の識別子が逐語的識別子 (§6.4.3) の場合、attribute_name は、type_name (§7.8) として解決されます。 結果が
System.Attribute
から派生した型でない場合は、コンパイル時エラーが発生します。 - それ以外の場合:
上記の 2 つの手順のうちの 1 つで System.Attribute
から派生した型が得られる場合、その型は attribute_name の結果になります。 それ以外の場合は、コンパイル時エラーが発生します。
例: このサフィックスの有無にかかわらず属性クラスが見つかった場合、あいまいさが存在し、コンパイル時エラーが発生します。 attribute_name の右端の識別子が逐語的識別子 (§6.4.3) である場合、サフィックスのない属性のみが一致するため、このようなあいまいさが解決されます。 例
[AttributeUsage(AttributeTargets.All)] public class Example : Attribute {} [AttributeUsage(AttributeTargets.All)] public class ExampleAttribute : Attribute {} [Example] // Error: ambiguity class Class1 {} [ExampleAttribute] // Refers to ExampleAttribute class Class2 {} [@Example] // Refers to Example class Class3 {} [@ExampleAttribute] // Refers to ExampleAttribute class Class4 {}
には、
Example
とExampleAttribute
という名前の 2 つの属性クラスが表示されます。 属性[Example]
は、Example
またはExampleAttribute
を参照する可能性があるため、あいまいです。 逐語的識別子を使用すると、このようなまれなケースで正確なインテントを指定できます。 属性[ExampleAttribute]
はあいまいではありません (ただし、ExampleAttributeAttribute
!という名前の属性クラスがあった場合です)。 クラスExample
の宣言が削除された場合、次のように、両方の属性がExampleAttribute
という名前の属性クラスを参照します。[AttributeUsage(AttributeTargets.All)] public class ExampleAttribute : Attribute {} [Example] // Refers to ExampleAttribute class Class1 {} [ExampleAttribute] // Refers to ExampleAttribute class Class2 {} [@Example] // Error: no attribute named “Example” class Class3 {}
終了サンプル
同じエンティティで single-use 属性クラスを複数回使用すると、コンパイル時エラーになります。
例: 例
[AttributeUsage(AttributeTargets.Class)] public class HelpStringAttribute : Attribute { public HelpStringAttribute(string value) { Value = value; } public string Value { get; } } [HelpString("Description of Class1")] [HelpString("Another description of Class1")] // multiple uses not allowed public class Class1 {}
は、single-use 属性クラスである
HelpString
をClass1
の宣言で複数回使用しようとするため、コンパイル時エラーが発生します。終了サンプル
式 E
は、次のすべてのステートメントが true の場合、attribute_argument_expression です。
-
E
の型は属性パラメーター型 (§22.2.4) です。 - コンパイル時に、
E
の値を次のいずれかに解決できます。
例:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Field)] public class TestAttribute : Attribute { public int P1 { get; set; } public Type P2 { get; set; } public object P3 { get; set; } } [Test(P1 = 1234, P3 = new int[]{1, 3, 5}, P2 = typeof(float))] class MyClass {} class C<T> { [Test(P2 = typeof(T))] // Error – T not a closed type. int x1; [Test(P2 = typeof(C<T>))] // Error – C<;T>; not a closed type. int x2; [Test(P2 = typeof(C<int>))] // Ok int x3; [Test(P2 = typeof(C<>))] // Ok int x4; }
終了サンプル
複数の部分で宣言された型の属性は、各部分の属性を不特定の順序で組み合わせることによって決定されます。 同じ属性が複数の部分に配置されている場合、その属性を型に複数回指定することと同じです。
例: 2 つの部分:
[Attr1, Attr2("hello")] partial class A {} [Attr3, Attr2("goodbye")] partial class A {}
は、次の単一宣言と同等です。
[Attr1, Attr2("hello"), Attr3, Attr2("goodbye")] class A {}
終了サンプル
型パラメーターの属性は、同じ方法で結合されます。
22.4 属性インスタンス
22.4.1 全般
属性インスタンスは、実行時に属性を表すインスタンスです。 ぞページ区政は、属性クラス、位置引数、名前付き引数で定義されます。 属性インスタンスは、位置引数と名前付き引数で初期化される属性クラスのインスタンスです。
属性インスタンスの取得には、次のサブクラスで説明するように、コンパイル時と実行時の両方の処理が含まれます。
22.4.2 属性のコンパイル
属性クラス のT
、positional_argument_listP
、named_argument_listN
、およびプログラム エンティティで指定されたエンティティ E
のコンパイルは、次の手順を実行してアセンブリ A
にコンパイルされます。
- 新しいフォーム の
T(P)
をコンパイルするには、コンパイル時の処理手順を実行します。 これらの手順では、コンパイル時エラーが発生するか、実行時に呼び出すことができるC
の インスタンス コンストラクターT
を特定します。 -
C
にパブリック アクセシビリティがない場合は、コンパイル時エラーが発生します。 -
の各
Arg
N
の場合:-
Name
を named_argument のArg
にします。 -
Name
は、T
の非静的読み取り/書き込みパブリック フィールドまたはプロパティを識別する必要があります。T
にそのようなフィールドまたはプロパティがない場合は、コンパイル時エラーが発生します。
-
-
positional_argument_list
P
内の任意の値または named_argument_listN
内のいずれかの値が、System.String
型で、値の形式が、Unicode Standard の定義に従っていない場合、コンパイルされた値が取得された実行時値と等しいかどうかが実装で定義されます (§22.4.3)。注: 例として、上位サロゲート UTF-16 コード ユニットを含む文字列の直後に下位サロゲート コードユニットが続かない文字列は形式が正しくありません。 注釈
- 属性を含むプログラムをコンパイルした結果として、コンパイラが出力するアセンブリに (属性の実行時インスタンス化のために) 属性クラス
T
、C
のインスタンス コンストラクターT
、positional_argument_listP
、named_argument_listN
、コンパイル時に完全に解決された値を含む関連するプログラム エンティティE
の情報を格納します。
22.4.3 属性インスタンスの実行時の取得
§22.4.2 で定義されている用語を使用すると、属性インスタンスは、T
、C
、P
、N
で示されます。E
A
に関連付けられた属性は、次の手順を実行すると、アセンブリから実行時に取得できます。
- コンパイル時に決定されたインスタンス コンストラクター を使用して、形式
new T(P)
のC
を実行するための実行時処理手順を実行します。 これらの手順では、例外が発生するか、O
のインスタンスT
が生成されます。 -
の各
Arg
N
の場合、次の順序に従います。-
Name
を named_argument のArg
にします。Name
が、O
の非静的パブリック読み取り/書き込みフィールドまたはプロパティを識別しない場合は、例外がスローされます。 -
Value
を のArg
の評価した結果にします。 -
Name
がO
でフィールドを識別しない場合は、このフィールドをValue
に設定します。 - それ以外の場合、Name は
O
のプロパティを識別します。 このプロパティを Value に設定します。 - 結果は、
O
、T
およびP
で初期化された属性クラスN
のインスタンスです。
-
注:
T
にC
、P
、N
、E
(およびA
に関連付ける) を格納する形式およびE
を指定するメカニズムおよび、T
から、C
、P
、N
、A
を取得するメカニズム (したがって、属性インスタンスが実行時にどのように取得されるか) は、この仕様の範囲外です。 注釈
22.5 予約済みの属性
22.5.1 全般
一部の属性が何らかの方法で言語に影響します。 以下のような属性があります。
-
System.AttributeUsageAttribute
(§22.5.2) は、属性クラスを使用する方法を記述するために使用されます。 -
System.Diagnostics.ConditionalAttribute
(§22.5.3) は、条件付きメソッドと条件付き属性クラスを定義するために使用される multi-use 属性クラスです。 この属性は、条件付きコンパイル シンボルをテストすることによって条件を示します。 -
System.ObsoleteAttribute
(§22.5.4) は、メンバーを廃止としてマークするために使用します。 -
System.Runtime.CompilerServices.AsyncMethodBuilderAttribute
(§22.5.5) は、非同期メソッドのタスク ビルダーを確立するために使用されます。 -
System.Runtime.CompilerServices.CallerLineNumberAttribute
(§22.5.6.2)、System.Runtime.CompilerServices.CallerFilePathAttribute
(§22.5.6.3)、およびSystem.Runtime.CompilerServices.CallerMemberNameAttribute
(§22.5.6.4) は、呼び出し元コンテキストに関する情報を省略可能なパラメーターに提供するために使用します。
Null 許容静的分析属性 (§22.5.7) を使用すると、null 許容および null 状態に対して生成される警告の正確性を向上できます (§8.9.5)。
実行環境では、C# プログラムの実行に影響を与える追加の実装定義属性が指定される場合があります。
22.5.2 AttributeUsage 属性
属性 AttributeUsage
は、属性クラスを使用できる方法を記述するために使用されます。
AttributeUsage
属性で修飾されたクラスは、直接または間接的に System.Attribute
から派生する必要があります。 それ以外の場合は、コンパイル時エラーが発生します。
注: この属性の使用例については、「§22.2.2」を参照してください。 注釈
22.5.3 条件付き属性
22.5.3.1 全般
属性 Conditional
により、条件付き属性および条件付き属性クラスの定義が有効になります。
22.5.3.2 条件付きメソッド
Conditional
属性で修飾されたメソッドは、条件付きメソッドです。 したがって、各条件付きメソッドは、Conditional
属性で宣言された条件付きコンパイル シンボルに関連付けられます。
例:
class Eg { [Conditional("ALPHA")] [Conditional("BETA")] public static void M() { // ... } }
は、
Eg.M
とALPHA
の 2 つの条件付きコンパイル シンボルに関連付けられた条件付きメソッドとしてBETA
を宣言します。終了サンプル
呼び出し時に関連付けられている条件付きコンパイル シンボルの 1 つ以上が定義されている場合は、条件付きメソッドの呼び出しが含まれます。それ以外の場合、呼び出しは省略されます。
条件付きメソッドには、次の制限が適用されます。
- 条件付きメソッドは、class_declaration または struct_declaration 内のメソッドである必要があります。
Conditional
属性がインターフェイス宣言のメソッドで指定されている場合、コンパイル時エラーが発生します。 - 条件付きメソッドには
void
の戻り値の型があります。 - 条件付きメソッドは、
override
修飾子でマークされません。 ただし、条件付きメソッドは、virtual
修飾子でマークできます。 このようなメソッドのオーバーライドは暗黙的に条件付きで、Conditional
属性で明示的にマークすることはできません。 - 条件付きメソッドは、インターフェイス メソッドの実装ではありません。 それ以外の場合は、コンパイル時エラーが発生します。
- 条件付きメソッドのパラメーターは、出力パラメーターにすることはできません。
さらに、条件付きメソッドからデリゲートが作成されると、コンパイル時エラーが発生します。
例: 例
#define DEBUG using System; using System.Diagnostics; class Class1 { [Conditional("DEBUG")] public static void M() { Console.WriteLine("Executed Class1.M"); } } class Class2 { public static void Test() { Class1.M(); } }
は、
Class1.M
を条件付きメソッドとして宣言します。Class2
のTest
メソッドは、このメソッドを呼び出します。 条件付きコンパイル シンボルDEBUG
が定義されているため、Class2.Test
が呼び出されると、M
が呼び出されます。 シンボルDEBUG
が定義されていない場合、Class2.Test
はClass1.M
を呼び出しません。終了サンプル
条件付きメソッドの呼び出しの包含または除外は、呼び出しの時点で条件付きコンパイル シンボルによって制御されることを理解しておくことが重要です。
例: 次のコード例の内容:
// File Class1.cs: using System; using System.Diagnostics; class Class1 { [Conditional("DEBUG")] public static void F() { Console.WriteLine("Executed Class1.F"); } } // File Class2.cs: #define DEBUG class Class2 { public static void G() { Class1.F(); // F is called } } // File Class3.cs: #undef DEBUG class Class3 { public static void H() { Class1.F(); // F is not called } }
各クラス
Class2
およびClass3
には、Class1.F
が定義されているかどうかに基づく条件付きメソッドDEBUG
の呼び出しが含まれます。 このシンボルはClass2
のコンテキストで定義されますが、Class3
では定義されていないため、F
のClass2
の呼び出しは含まれますが、F
のClass3
の呼び出しは省略されます。終了サンプル
継承チェーンで条件付きメソッドを使用すると、混乱を招く可能性があります。 形式 base
の base.M
経由で呼び出された条件付きメソッドは、通常の条件付きメソッド呼び出しルールが適用されます。
例: 次のコード例の内容:
// File Class1.cs using System; using System.Diagnostics; class Class1 { [Conditional("DEBUG")] public virtual void M() => Console.WriteLine("Class1.M executed"); } // File Class2.cs class Class2 : Class1 { public override void M() { Console.WriteLine("Class2.M executed"); base.M(); // base.M is not called! } } // File Class3.cs #define DEBUG class Class3 { public static void Main() { Class2 c = new Class2(); c.M(); // M is called } }
Class2
には、基底クラスで定義されているM
の呼び出しが含まれています。 基本メソッドは、未定義のシンボルDEBUG
の存在に基づいて条件付きであるため、この呼び出しは省略されます。 したがって、メソッドはコンソール "Class2.M executed
" にのみ書き込みます。 pp_declaration を慎重に使用することで、このような問題を排除できます。終了サンプル
22.5.3.3 条件付き属性クラス
1 つ以上の 属性で修飾された属性クラス (Conditional
) は、条件付き属性クラスです。 したがって、各条件付きクラスは、Conditional
属性で宣言された条件付きコンパイル シンボルに関連付けられます。
例:
[Conditional("ALPHA")] [Conditional("BETA")] public class TestAttribute : Attribute {}
TestAttribute
は、条件付きコンパイルシンボルALPHA
およびBETA
に関連付けられた条件付き属性クラスとして宣言します。終了サンプル
条件属性の属性仕様 (§22.3) は、関連付けられている条件付きコンパイル シンボルの 1 つ以上が仕様の時点で定義されている場合に含まれます。それ以外の場合、属性の指定は省略されます。
条件付き属性クラスの属性仕様の包含または除外は、仕様の時点で条件付きコンパイル シンボルによって制御されることに注意してください。
例: この例では
// File Test.cs: using System; using System.Diagnostics; [Conditional("DEBUG")] public class TestAttribute : Attribute {} // File Class1.cs: #define DEBUG [Test] // TestAttribute is specified class Class1 {} // File Class2.cs: #undef DEBUG [Test] // TestAttribute is not specified class Class2 {}
クラス
Class1
とClass2
はそれぞれ、属性Test
で修飾されます。これは、DEBUG
が定義されているかどうかに基づいて条件付きになります。 このシンボルは、Class1
のコンテキストで定義されており、Class2
ではないため、Class1
のテスト属性の仕様が含まれる一方で、Test
のClass2
属性の仕様は省略されます。終了サンプル
22.5.4 Obsolete 属性
Obsolete
属性は、使用されなくなった型の型とメンバーをマークするために使用されます。
プログラムが Obsolete
属性で修飾された型またはメンバーを使用していると、コンパイラは警告またはエラーを発行します。 具体的には、エラー パラメーターが指定されていない場合、またはエラー パラメーターが指定されていて値が false
の場合、コンパイラは警告を発行します。 エラー パラメーターが指定されていて値が true
の場合、コンパイラはエラーを発行します。
例: 次のコード例の内容:
[Obsolete("This class is obsolete; use class B instead")] class A { public void F() {} } class B { public void F() {} } class Test { static void Main() { A a = new A(); // Warning a.F(); } }
クラス
A
は、Obsolete
属性で修飾されます。A
のMain
を使用すると、"このクラスは廃止されました。代わりにクラスB
を使用してください。" という指定されたメッセージが含まれる警告が表示されます。終了サンプル
22.5.5 AsyncMethodBuilder 属性
この属性については、 §15.14.1 を参照してください。
22.5.6 Caller-info 属性
22.5.6.1 全般
ログ記録やレポートなどの目的で、関数メンバーが呼び出し元のコードに関する特定のコンパイル時情報を取得すると便利な場合があります。 caller-info 属性は、このような情報を透過的に渡す方法を提供します。
省略可能なパラメーターにいずれかの caller-info 属性が注釈付けされている場合、呼び出しで対応する引数を省略しても、既定のパラメーター値が置き換えられるとは限りません。 代わりに、呼び出し元のコンテキストに関する指定された情報が使用可能な場合、その情報は引数値として渡されます。
例:
public void Log( [CallerLineNumber] int line = -1, [CallerFilePath] string path = null, [CallerMemberName] string name = null ) { Console.WriteLine((line < 0) ? "No line" : "Line "+ line); Console.WriteLine((path == null) ? "No file path" : path); Console.WriteLine((name == null) ? "No member name" : name); }
引数を指定しない
Log()
を呼び出すと、呼び出しの行番号とファイル パス、および呼び出しが発生したメンバーの名前が出力されます。終了サンプル
caller-info 報属性は、デリゲート宣言を含め、任意の場所で省略可能なパラメーターで発生する可能性があります。 ただし、特定の caller-info 属性には、属性として使用できるパラメーターの型に制限があるため、置換された値からパラメーター型への暗黙的な変換が常に存在します。
部分メソッド宣言の定義部分と実装部分の両方のパラメーターに対して同じ caller-info 属性を持つとエラーになります。 定義パーツ内の caller-info 属性のみが適用されますが、実装パーツでのみ発生する caller-info 属性は無視されます。
呼び出し元の情報は、オーバーロードの解決には影響しません。 属性付き省略可能パラメーターは呼び出し元のソース コードから引き続き省略されるため、オーバーロードの解決では、省略された他の省略可能なパラメーター (§12.6.4) を無視するのと同じ方法でこれらのパラメーターが無視されます。
呼び出し元情報は、ソース コードで関数が明示的に呼び出された場合にのみ置き換わります。 暗黙的な親コンストラクター呼び出しなどの暗黙的な呼び出しにはソースの場所がないため、呼び出し元の情報は置き換られません。 また、動的にバインドされた呼び出しは、呼び出し元情報に代わるものではありません。 このような場合に caller-info 属性パラメーターを省略すると、パラメーターの指定された既定値が代替使用されます。
例外の 1 つとして、クエリ式が挙げられます。 これらは構文拡張とみなされ、呼び出し元が展開して、caller-info 属性を持つ省略可能なパラメーターを省略すると、呼び出し元情報が置き換えられます。 使用される場所は、呼び出しが生成されたクエリ句の場所です。
指定されたパラメーターに複数の caller-info 属性が指定されている場合は、CallerLineNumber
、CallerFilePath
、CallerMemberName
の順序で認識されます。 次のパラメーター宣言について考えてみましょう。
[CallerMemberName, CallerFilePath, CallerLineNumber] object p = ...
CallerLineNumber
が優先され、他の 2 つの属性は無視されます。
CallerLineNumber
を省略すると、CallerFilePath
が優先され、CallerMemberName
が無視されます。 これらの属性の字句の順序は関係ありません。
22.5.6.2 CallerLineNumber 属性
定数値 System.Runtime.CompilerServices.CallerLineNumberAttribute
からパラメーターの型への標準の暗黙的な変換 (§10.4.2) がある場合、省略可能なパラメーターで属性 int.MaxValue
が許可されます。 これにより、その値までの負以外の行番号をエラーなしで渡すことができます。
ソース コード内の場所からの関数呼び出しで、CallerLineNumberAttribute
を使用した省略可能なパラメーターが省略されている場合、その場所の行番号を表す数値リテラルが、既定のパラメーター値ではなく、呼び出しの引数として使用されます。
呼び出しが複数行にまたがる場合、選択された行は実装に依存します。
行番号は、#line
ディレクティブ (§6.5.8) の影響を受ける場合があります。
22.5.6.3 CallerFilePath 属性
System.Runtime.CompilerServices.CallerFilePathAttribute
からパラメーターの型への標準の暗黙的な変換 (§10.4.2) がある場合、省略可能なパラメーターで属性 string
が許可されます。
ソース コード内の場所からの関数呼び出しで、CallerFilePathAttribute
を使用した省略可能なパラメーターが省略されている場合、その場所のファイル パスを表す文字列リテラルが、既定のパラメーター値ではなく、呼び出しの引数として使用されます。
ファイル パスの形式は実装に依存します。
ファイル パスは、#line
ディレクティブ (§6.5.8) の影響を受ける場合があります。
22.5.6.4 CallerMemberName 属性
System.Runtime.CompilerServices.CallerMemberNameAttribute
からパラメーターの型への標準の暗黙的な変換 (§10.4.2) がある場合、省略可能なパラメーターで属性 string
が許可されます。
関数メンバーの本文内の場所または、関数メンバー自体に適用された属性内またはその戻り値の型、ソース コード内のパラメーターまたは型パラメーターからの関数呼び出しが CallerMemberNameAttribute
を使用して省略された場合、そのメンバーを表す文字列リテラルは、既定のパラメーター値ではなく、呼び出しへの引数として使用されます。
ジェネリック メソッド内で発生する呼び出しの場合、型パラメーター リストを使用せずに、メソッド名自体のみが使用されます。
明示的なインターフェイス メンバーの実装内で発生する呼び出しの場合は、前のインターフェイス修飾なしで、メソッド名自体のみが使用されます。
プロパティまたはイベント アクセサー内で発生する呼び出しの場合、使用されるメンバー名はプロパティまたはイベント自体のメンバー名です。
インデクサー アクセサー内で発生する呼び出しの場合、使用されるメンバー名は、存在する場合は、インデクサー メンバーの IndexerNameAttribute
(§22.6) で指定されるものか、それ以外の場合は、既定名 Item
になります。
フィールド初期化子またはイベント初期化子内で発生する呼び出しの場合、使用されるメンバー名は初期化されるフィールドまたはイベントの名前です。
インスタンス コンストラクター、静的コンストラクター、ファイナライザー、および演算子の宣言内で発生する呼び出しの場合、使用されるメンバー名は実装に依存します。
22.5.7 code-analysis属性
22.5.7.1 全般
このサブクラスの属性は、null 値の許容と null 状態の診断 (§8.9.5) を提供するコンパイラをサポートするための追加情報を提供するために使用されます。 コンパイラは、null 状態の診断を実行する必要はありません。 これらの属性の有無は、言語やプログラムの動作には影響しません。 null 状態の診断を提供しないコンパイラは、これらの属性の存在を読み取り、無視する必要があります。 null 状態の診断を提供するコンパイラは、診断を通知するために使用するこれらの属性に対して、このサブクラスで定義されている意味を使用する必要があります。
code-analysis 属性は、名前空間 System.Diagnostics.CodeAnalysis
で宣言されます。
属性 | 意味 |
---|---|
AllowNull (§22.5.7.2) |
null 非許容の引数を null にすることができます。 |
DisallowNull (§22.5.7.3) |
null 許容の引数を null にすることはできません。 |
MaybeNull (§22.5.7.6) |
null 非許容の戻り値は null である可能性があります。 |
NotNull (§22.5.7.8) |
null 許容の戻り値が null になることはありません。 |
MaybeNullWhen (§22.5.7.7) |
メソッドから指定された bool 値が返されるとき、null 非許容の引数が null である可能性があります。 |
NotNullWhen (§22.5.7.10) |
メソッドから指定された bool 値が返されるとき、null 許容の引数は null になりません。 |
NotNullIfNotNull (§22.5.7.9) |
指定されたパラメーターの引数が null でない場合、戻り値は null ではありません。 |
DoesNotReturn (§22.5.7.4) |
このメソッドは返されません。 |
DoesNotReturnIf (§22.5.7.5) |
関連付けられた bool パラメーターに指定された値がある場合、このメソッドから制御が返されることはありません。 |
§22.5.7.1 の次のサブクラウスは条件付きで規範的です。
22.5.7.2 AllowNull 属性
対応する型で禁止されていても、null 値の入力を許可することを指定します。
例: 切な既定値が設定されているため、
null
を返さない、次の読み取りおよび書き込みプロパティを考慮します。 ただし、ユーザーは set アクセサーに null を指定して、プロパティをその既定値に設定できます。#nullable enable public class X { [AllowNull] public string ScreenName { get => _screenName; set => _screenName = value ?? GenerateRandomScreenName(); } private string _screenName = GenerateRandomScreenName(); private static string GenerateRandomScreenName() => ...; }
そのプロパティの set アクセサーの次の使用を考えると
var v = new X(); v.ScreenName = null; // may warn without attribute AllowNull
この属性が指定されていないと、null 非許容型のプロパティが null 値に設定されているように解釈され、コンパイラが警告を生成する可能性があります。 属性が存在すると、その警告は抑制されます。 終了サンプル
22.5.7.3 DisallowNull 属性
対応する型で許可されいても、null 値の入力を禁止することを指定します。
例: null が既定値ですが、クライアントは null 以外の値にのみ設定できる次のプロパティを考慮します。
#nullable enable public class X { [DisallowNull] public string? ReviewComment { get => _comment; set => _comment = value ?? throw new ArgumentNullException(nameof(value), "Cannot set to null"); } private string? _comment = default; }
get アクセサは既定値の
null
を返す可能性があるため、アクセス前にチェックが必要であるとコンパイラから警告される場合があります。 さらに、null である可能性があっても、呼び出し元で明示的に null に設定できないことを呼び出し元に警告します。 終了サンプル
22.5.7.4 DoesNotReturn 属性
特定のメソッドが返されないように指定します。
例: 以下を確認してください。
public class X { [DoesNotReturn] private void FailFast() => throw new InvalidOperationException(); public void SetState(object? containedField) { if ((!isInitialized) || (containedField == null)) { FailFast(); } // null check not needed. _field = containedField; } private bool isInitialized = false; private object _field; }
この属性の存在は、コンパイラにとってさまざまな点で役立ちます。 まず、メソッドが例外をスローせずに終了できるパスがある場合、コンパイラは警告を発行できます。 2 番目に、コンパイラは、適切な catch 句が見つかるまで、そのメソッドの呼び出し後のすべてのコードで null 許容警告を抑制できます。 3 つ目では、到達できないコードが null 状態に影響することはありません。
属性は、この属性の存在に基づいて到達可能性 (§13.2) または確定代入 (§9.4) 分析を変更しません。 これは、null 許容の警告に影響を与えるためだけに使用されます。 終了サンプル
22.5.7.5 DoesNotReturnIf 属性
関連付けられている bool
パラメーターに指定した値がある場合、特定のメソッドが返されないように指定します。
例: 以下を確認してください。
#nullable enable public class X { private void ThrowIfNull([DoesNotReturnIf(true)] bool isNull, string argumentName) { if (!isNull) { throw new ArgumentException(argumentName, $"argument {argumentName} can't be null"); } } public void SetFieldState(object containedField) { ThrowIfNull(containedField == null, nameof(containedField)); // unreachable code when "isInitialized" is false: _field = containedField; } private bool isInitialized = false; private object _field = default!; }
終了サンプル
22.5.7.6 MaybeNull 属性
null 許容以外の戻り値を null にするように指定します。
例: 次の一般的なメソッドを考慮します。
#nullable enable public T? Find<T>(IEnumerable<T> sequence, Func<T, bool> predicate) { ... }
このコードの考え方は、
T
がstring
に置き換えられた場合、T?
は null 許容注釈になるということです。 ただし、T
は参照型に制約されていないため、このコードは有効ではありません。 ただし、この属性を追加すると、問題が解決します。#nullable enable [return: MaybeNull] public T Find<T>(IEnumerable<T> sequence, Func<T, bool> predicate) { ... }
属性は、呼び出し元にコントラクトはnull 非許容型を意味するが、戻り値は実際には
null
であることを通知します。 終了サンプル
22.5.7.7 MaybeNullWhen 属性
メソッドが、指定された null
値を返す場合、null 許容以外の引数を bool
にすることを指定します。 これは MaybeNull
属性 (§22.5.7.6) に似ていますが、指定された戻り値のパラメーターが含まれています。
22.5.7.8 NotNull 属性
メソッドが (スローではなく) 返された場合に null 許容値が null
されないように指定します。
例: 以下を確認してください。
#nullable enable public static void ThrowWhenNull([NotNull] object? value, string valueExpression = "") => _ = value ?? throw new ArgumentNullException(valueExpression); public static void LogMessage(string? message) { ThrowWhenNull(message, nameof(message)); Console.WriteLine(message.Length); }
null 参照型が有効になっている場合、メソッド
ThrowWhenNull
は警告なしでコンパイルされます。 そのメソッドが戻ったときに、value
引数がnull
でないことが保証されます。 ただし、null 参照を使用してThrowWhenNull
を呼び出すことができます。 終了サンプル
22.5.7.9 NotNullIfNotNull 属性
指定されたパラメーターの引数が null
ではない場合、戻り値を null
にしないように指定します。
例: 戻り値の null 状態は、1 つまたは複数の引数の null 状態に依存するできます。 特定の引数が
null
でないときにメソッドが常に null 以外の値を返す場合のコンパイラの分析を支援するために、NotNullIfNotNull
属性を使用できます。 次のメソッドがあるとします。#nullable enable string GetTopLevelDomainFromFullUrl(string url) { ... }
url
引数がnull
でない場合、null
は、返されません。 null 許容参照を有効にすると、API で null 引数が受け入れられることがない場合、そのシグネチャは正常に機能します。 しかし、引数が null である可能性がある場合は、戻り値も null である可能性があります。 そのコントラクトを正しく表現するには、次のようにこのメソッドに注釈を付けます。#nullable enable [return: NotNullIfNotNull("url")] string? GetTopLevelDomainFromFullUrl(string? url) { ... }
終了サンプル
22.5.7.10 NotNullWhen 属性
メソッドが、指定された null
値を返す場合、null 許容引数を bool
にしないように指定します。
例: ライブラリ メソッド
String.IsNullOrEmpty(String)
は、引数がtrue
または空の文字列である場合にnull
を返します。 これは null チェックの形式です。メソッドがfalse
を返す場合、呼び出し元は引数を null チェックする必要はありません。 このようなメソッドを null 許容にするには、パラメーターの型を null 許容参照型にし、NotNullWhen 属性を追加します。#nullable enable bool IsNullOrEmpty([NotNullWhen(false)] string? value) { ... }
終了サンプル
22.6 相互運用のための属性
他の言語との相互運用のために、インデックス付きプロパティを使用してインデクサーを実装できます。 インデクサーに IndexerName
属性がない場合は、既定で 名前 Item
が使用されます。
IndexerName
属性を使用すると、開発者はこの既定値をオーバーライドし、別の名前を指定できます。
例: 既定では、インデクサーの名前は
Item
です。 これは、次のようにオーバーライドされます。[System.Runtime.CompilerServices.IndexerName("TheItem")] public int this[int index] { get { ... } set { ... } }
これで、インデクサーの名前が
TheItem
になります。終了サンプル
ECMA C# draft specification