次の方法で共有


22 属性

22.1 全般

C# 言語の多くを使用すると、プログラマはプログラムで定義されているエンティティに関する宣言型情報を指定できます。 たとえば、クラス内のメソッドのアクセシビリティは、method_modifierpublicprotectedinternal、およびprivateで修飾することによって指定されます。

C# を使用すると、プログラマは attributes と呼ばれる新しい種類の宣言型情報を発明できます。 プログラマは、さまざまなプログラム エンティティに属性をアタッチし、実行時環境で属性情報を取得できます。

: たとえば、フレームワークでは、特定のプログラム要素 (クラスやメソッドなど) に配置できる HelpAttribute 属性を定義して、それらのプログラム要素からドキュメントへのマッピングを提供できます。 end note

属性は、位置指定パラメーターと名前付きパラメーター (§22.2.3) を持つ属性クラス (§22.2) の宣言によって定義されます。 属性は、属性仕様 (§22.3) を使用して C# プログラムのエンティティにアタッチされ、実行時に属性インスタンスとして取得できます (§22.4)。

22.2 属性クラス

22.2.1 全般

抽象クラス System.Attributeから派生するクラスは、直接的または間接的に関係なく、 attribute クラスです。 属性クラスの宣言は、プログラム エンティティに配置できる新しい種類の属性を定義します。 慣例により、属性クラスには Attribute のサフィックスを付けた名前が付けられます。 属性の使用には、このサフィックスを含めるか省略できます。

ジェネリック クラス宣言では、直接基底クラスまたは間接基底クラスとして System.Attribute を使用することはできません。

例:

public class B : Attribute {}
public class C<T> : B {} // Error – generic cannot be an attribute

end の例

22.2.2 属性の使用

属性 AttributeUsage (§22.5.2) は、属性クラスの使用方法を記述するために使用されます。

AttributeUsage には位置パラメーター (§22.2.3) があり、属性クラスで使用できるプログラム エンティティの種類を指定できます。

: 次の例では、SimpleAttributeinterface_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 {...}

end の例

AttributeUsageには、 と呼ばれる名前付きパラメーター (AllowMultiple) があります。これは、特定のエンティティに対して属性を複数回指定できるかどうかを示します。 属性クラスの AllowMultiple が true の場合、その属性クラスは multi-use 属性クラスであり、エンティティで複数回指定できます。 属性クラスの AllowMultiple が false または指定されていない場合、その属性クラスは single-use 属性クラスであり、エンティティに対して最大で 1 回指定できます。

: 次の例では、 AuthorAttribute という名前の複数使用属性クラスを定義し、 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 
{
    ...
}

end の例

AttributeUsageには、 と呼ばれる別の名前付きパラメーター (Inherited) があります。これは、基底クラスで指定された属性がその基底クラスから派生したクラスによって継承されるかどうかを示します。 属性クラスの Inherited が true の場合、その属性は継承されます。 属性クラスの Inherited が false の場合、その属性は継承されません。 指定されていない場合、既定値は true です。

属性クラス X 次のように、 AttributeUsage 属性がアタッチされていません

class X : Attribute { ... }

は、次と同じです。

[AttributeUsage(
   AttributeTargets.All,
   AllowMultiple = false,
   Inherited = true)
]
class X : Attribute { ... }

22.2.3 位置指定パラメーターと名前付きパラメーター

属性クラスには、 positional parameters および named parameters を指定できます。 属性クラスの各パブリック インスタンス コンストラクターは、その属性クラスの位置指定パラメーターの有効なシーケンスを定義します。 属性クラスの静的でないパブリック読み取り/書き込みフィールドとプロパティはそれぞれ、属性クラスの名前付きパラメーターを定義します。 プロパティで名前付きパラメーターを定義する場合、そのプロパティにはパブリック get アクセサーとパブリック セット アクセサーの両方が含まれます。

: 次の例では、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
{
}

end の例

22.2.4 属性パラメーターの型

属性クラスの位置指定パラメーターと名前付きパラメーターの型は、 attribute パラメーター型に制限されます。これは次のとおりです。

  • 次のいずれかの種類: boolbytechardoublefloatintlongsbyteshortstringuintulongushort
  • object 型です。
  • System.Type 型です。
  • 列挙型。
  • 上記の型の 1 次元配列。
  • これらの型のいずれかを持たないコンストラクター引数またはパブリック フィールドは、属性指定の位置指定パラメーターまたは名前付きパラメーターとして使用しないでください。

22.3 属性の指定

属性の指定 は、以前に定義された属性をプログラム エンティティに適用することです。 属性は、プログラム エンティティに対して指定される追加の宣言情報の一部です。 属性は、グローバル スコープで指定できます (包含アセンブリまたはモジュールの属性を指定するため)、および type_declaration (§14.7)、 class_member_declaration (§15.3)、 interface_member_declaration (§18. 4)、 struct_member_declaration (§16.3)、 enum_member_declaration (§19.2)、 accessor_declaration (§15.7.3)、 event_accessor_declarations (§15.8)、 parameter_listの要素 (§15.6.2)、および type_parameter_listの要素 (§15.2.3)。

属性は、 attribute セクションで指定。 属性セクションは、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 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_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_sectionattribute_sectionで末尾のコンマを使用できます。 end note

attribute_nameは属性クラスを識別します。

属性をグローバル レベルで配置する場合は、 global_attribute_target_specifier が必要です。 global_attribute_targetが次の値と等しい場合:

  • assembly — ターゲットは包含アセンブリです。
  • module — ターゲットは包含モジュールです。

global_attribute_targetの他の値は使用できません。

標準化された attribute_target 名は、 eventfieldmethodparampropertyreturntype、および typevarです。 これらのターゲット名は、次のコンテキストでのみ使用されます。

  • event — イベント。
  • field — フィールド。 フィールドに似たイベント (アクセサーのないイベント) (§15.8.2) と自動的に実装されるプロパティ (§15.7.4) も、このターゲットを持つ属性を持つことができます。
  • method — コンストラクター、ファイナライザー、メソッド、演算子、プロパティの get アクセサーと set アクセサー、インデクサーの get アクセサーと set アクセサー、およびイベントの追加および削除アクセサー。 フィールドに似たイベント (アクセサーのないイベント) も、このターゲットの属性を持つことができます。
  • param — プロパティ セット アクセサー、インデクサー セット アクセサー、イベントの追加および削除アクセサー、およびコンストラクター、メソッド、および演算子のパラメーター。
  • property — プロパティとインデクサー。
  • return — デリゲート、メソッド、演算子、プロパティ取得アクセサー、およびインデクサー get アクセサー。
  • type — デリゲート、クラス、構造体、列挙型、およびインターフェイス。
  • typevar — 型パラメーター。

特定のコンテキストでは、複数のターゲットに対する属性の指定が許可されます。 プログラムは、 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 — ターゲットは、1 つの暗黙的なパラメーターです。
  • 自動的に実装されるプロパティ宣言の属性の場合、既定のターゲットはプロパティです。 それ以外の場合、 attribute_target が次の値に等しい場合:
    • field — ターゲットは、プロパティのコンパイラによって生成されたバッキング フィールドです
  • 既定のターゲットがイベント宣言 event_accessor_declarations 省略するイベント宣言で指定された属性の場合。 それ以外の場合、 attribute_target が次の値に等しい場合:
    • event — ターゲットはイベント宣言です
    • field — ターゲットはフィールドです。
    • method — ターゲットはメソッドです。
  • 既定のターゲットがメソッド event_accessor_declarations 省略しないイベント宣言の場合。
    • method — ターゲットは関連付けられたメソッドです。
    • param — ターゲットは、1 つのパラメーターです。

他のすべてのコンテキストでは、 attribute_target_specifier を含めることは許可されますが、不要です。

: クラス宣言には、指定子 typeを含めるか省略できます。

[type: Author("Brian Kernighan")]
class Class1 {}

[Author("Dennis Ritchie")]
class Class2 {}

end example

実装は他の attribute_targetを受け入れることができ、その目的は実装が定義されています。 このような attribute_target を認識しない実装では、警告を発行し、含まれている attribute_sectionを無視する必要があります。

慣例により、属性クラスには Attribute のサフィックスを付けた名前が付けられます。 attribute_nameには、このサフィックスを含めるか省略できます。 具体的には、 attribute_name は次のように解決されます。

  • attribute_nameの右端の識別子が逐語的識別子 (§6.4.3) の場合、attribute_nametype_name (§7.8) として解決されます。 結果が System.Attributeから派生した型でない場合は、コンパイル時エラーが発生します。
  • それ以外の場合、
    • エラーが抑制されない限り、 attribute_nametype_name (§7.8) として解決されます。 この解決が成功し、 System.Attribute から派生した型になった場合、型はこの手順の結果になります。
    • Attribute文字はattribute_nameの右端の識別子に追加され、結果のトークン文字列は、エラーが抑制されない限り、type_name (§7.8) として解決されます。 この解決が成功し、 System.Attribute から派生した型になった場合、型はこの手順の結果になります。

上記の 2 つの手順のうちの 1 つで System.Attributeから派生した型が得られる場合、その型は attribute_nameの結果になります。 それ以外の場合は、コンパイル時エラーが発生します。

: このサフィックスの有無にかかわらず属性クラスが見つかった場合、あいまいさが存在し、コンパイル時エラーが発生します。 attribute_nameのスペルが最もidentifierが逐語的識別子 (§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 {}

には、 ExampleExampleAttribute という名前の 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 {}

end の例

同じエンティティで単一使用属性クラスを複数回使用すると、コンパイル時エラーになります。

: 例

[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 {}

は、単一使用属性クラスである HelpStringClass1の宣言で複数回使用しようとするため、コンパイル時エラーが発生します。

end の例

E は、次のすべてのステートメントが true の場合に attribute_argument_expression です。

  • Eの型は属性パラメーター型 (§22.2.4) です。
  • コンパイル時に、 E の値を次のいずれかに解決できます。
    • 定数値。
    • 非ジェネリック型を指定するSystem.Type (§12.8.18) を使用して取得された オブジェクト。 閉じた構築型 (§8.4.3)、またはバインドされていないジェネリック型 (§8.4.4) ですが、オープン型 (§8.4.3)。
    • attribute_argument_expressionの 1 次元配列。

例:

[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;
}

end の例

複数の部分で宣言された型の属性は、各部分の属性を不特定の順序で組み合わせることによって決定されます。 同じ属性が複数の部分に配置されている場合、その属性を型に複数回指定することと同じです。

: 2 つの部分:

[Attr1, Attr2("hello")]
partial class A {}

[Attr3, Attr2("goodbye")]
partial class A {}

は、次の 1 つの宣言と同じです。

[Attr1, Attr2("hello"), Attr3, Attr2("goodbye")]
class A {}

end の例

型パラメーターの属性は、同じ方法で結合されます。

22.4 属性インスタンス

22.4.1 全般

attribute インスタンスは、実行時に属性を表すインスタンスです。 属性は、属性クラス、位置引数、および名前付き引数で定義されます。 属性インスタンスは、位置引数と名前付き引数で初期化される属性クラスのインスタンスです。

属性インスタンスの取得には、次のサブクラスで説明するように、コンパイル時と実行時の両方の処理が含まれます。

22.4.2 属性のコンパイル

attribute属性クラスのTpositional_argument_listPnamed_argument_listN、およびプログラム エンティティで指定されたEのコンパイルは、次の手順を使用してアセンブリ Aにコンパイルされます。

  • 新しいフォームの object_creation_expression をコンパイルするには、コンパイル時の処理手順に従 T(P)。 これらの手順では、コンパイル時エラーが発生するか、実行時に呼び出すことができるCTインスタンス コンストラクターを特定します。
  • Cにパブリック アクセシビリティがない場合は、コンパイル時エラーが発生します。
  • の各ArgNについて:
    • Name識別子Argします。
    • Name は、 Tの非静的読み取り/書き込みパブリック フィールドまたはプロパティを識別する必要があります。 Tにそのようなフィールドまたはプロパティがない場合は、コンパイル時エラーが発生します。
  • positional_argument_listP内の値またはnamed_argument_listN内のいずれかの値がSystem.String型で、Unicode 標準で定義されているとおりに適切な形式でない場合、コンパイルされた値が取得された実行時値と等しいかどうかが実装で定義されます (§22.4.3)。

    : 例として、上位サロゲート UTF-16 コード単位を含む文字列の直後に低サロゲート コードユニットが続かない文字列は整形式ではありません。 end note

  • 属性を含むプログラムをコンパイルした結果、コンパイラが出力するアセンブリ出力に、属性クラス TCのインスタンス コンストラクターTpositional_argument_listPnamed_argument_listN、および関連するプログラム エンティティのEに関する情報を格納します。値はコンパイル時に完全に解決されます。

22.4.3 属性インスタンスの実行時の取得

§22.4.2 で定義されている用語を使用してTCP、およびNで表され、Eに関連付けられた属性インスタンスは、次の手順を使用してアセンブリ Aから実行時に取得できます。

  • コンパイル時に決定されたインスタンス コンストラクターのと値を使用して、フォーム new T(P)Cを実行するには、実行時の処理手順に従います。 これらの手順では、例外が発生するか、Oのインスタンス Tが生成されます。
  • ArgNごとに、次の順序で指定します。
    • Name識別子Argします。 NameOの静的でないパブリック読み取り/書き込みフィールドまたはプロパティを識別しない場合は、例外がスローされます。
    • ValueArgを評価した結果になります。
    • NameOのフィールドを識別する場合は、このフィールドを Value に設定します。
    • それ以外の場合、Name は Oのプロパティを識別します。 このプロパティを Value に設定します。
    • 結果は、OTnamed_argument_listPで初期化された属性クラスのインスタンスN

:TCPN (およびEに関連付ける) をAに格納するための形式と、Eを指定してTからCPNAを取得するメカニズム (したがって、実行時に属性インスタンスを取得する方法) は、この仕様の範囲外です。 end note

: CLI の実装では、§22.2.3 でサンプル プログラムをコンパイルして作成されたアセンブリ内のHelp属性インスタンス次のプログラムを使用して取得できます。

public sealed class InterrogateHelpUrls
{
    public static void Main(string[] args)
    {
        Type helpType = typeof(HelpAttribute);
        string assemblyName = args[0];
        foreach (Type t in Assembly.Load(assemblyName).GetTypes()) 
        {
            Console.WriteLine($"Type : {t}");
            var attributes = t.GetCustomAttributes(helpType, false);
            var helpers = (HelpAttribute[]) attributes;
            foreach (var helper in helpers)
            {
                Console.WriteLine($"\tUrl : {helper.Url}");
            }
        }
    }
}

end の例

22.5 予約済み属性

22.5.1 全般

いくつかの属性が何らかの方法で言語に影響します。 以下のような属性があります。

  • System.AttributeUsageAttribute (§22.5.2) は、属性クラスを使用する方法を記述するために使用されます。
  • System.Diagnostics.ConditionalAttribute (§22.5.3) は、条件付きメソッドと条件付き属性クラスを定義するために使用されるマルチユース属性クラスです。 この属性は、条件付きコンパイル シンボルをテストすることによって条件を示します。
  • 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) を使用すると、nullabilities および null 状態に対して生成される警告の正確性を向上させることができます (§8.9.5)。

実行環境では、C# プログラムの実行に影響を与える追加の実装定義属性が提供される場合があります。

22.5.2 AttributeUsage 属性

属性 AttributeUsage は、属性クラスを使用できる方法を記述するために使用されます。

AttributeUsage属性で修飾されたクラスは、直接または間接的にSystem.Attributeから派生する必要があります。 それ以外の場合は、コンパイル時エラーが発生します。

: この属性の使用例については、 §22.2.2 を参照してください。 end note

22.5.3 条件属性

22.5.3.1 全般

属性 Conditional により、 conditional メソッド および conditional 属性クラスの定義が可能になります。

22.5.3.2 条件付きメソッド

Conditional属性で修飾されたメソッドは、条件付きメソッドです。 したがって、各条件付きメソッドは、 Conditional 属性で宣言された条件付きコンパイル シンボルに関連付けられます。

例:

class Eg
{
    [Conditional("ALPHA")]
    [Conditional("BETA")]
    public static void M()
    {
        // ...
    }
}

Eg.Mは、ALPHABETAの 2 つの条件付きコンパイル シンボルに関連付けられた条件付きメソッドとして宣言します。

end の例

呼び出し時に関連付けられている条件付きコンパイル シンボルの 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's Test メソッドはこのメソッドを呼び出します。 条件付きコンパイル シンボル DEBUG が定義されているため、 Class2.Test が呼び出されると、 Mが呼び出されます。 シンボル DEBUG が定義されていない場合、 Class2.TestClass1.Mを呼び出しません。

end の例

条件付きメソッドの呼び出しの包含または除外は、呼び出しの時点で条件付きコンパイル シンボルによって制御されることを理解しておくことが重要です。

: 次のコード内

// 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では定義されていないため、FClass2の呼び出しは含まれますが、FClass3の呼び出しは省略されます。

end の例

継承チェーンで条件付きメソッドを使用すると、混乱を招く可能性があります。 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の慎重な使用は、このような問題を排除することができます.

end の例

22.5.3.3 条件付き属性クラス

1 つ以上の属性で修飾された属性クラス (Conditional) は、条件付き属性クラスです。 したがって、条件付き属性クラスは、その Conditional 属性で宣言された条件付きコンパイル シンボルに関連付けられます。

例:

[Conditional("ALPHA")]
[Conditional("BETA")]
public class TestAttribute : Attribute {}

TestAttributeは、条件付きコンパイルシンボルALPHAおよびBETAに関連付けられた条件付き属性クラスとして宣言します。

end の例

条件属性の属性仕様 (§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 属性の指定は含まれますが、TestClass2属性の指定は省略されます。

end の例

22.5.4 廃止された属性

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属性で修飾されます。 AMainを使用するたびに、指定されたメッセージ "このクラスは廃止されました。代わりにクラス Bを使用してください。

end の例

22.5.5 AsyncMethodBuilder 属性

この属性については、 §15.15.1 で説明します。

22.5.6 呼び出し元情報属性

22.5.6.1 全般

ログ記録やレポートなどの目的で、関数メンバーが呼び出し元のコードに関する特定のコンパイル時情報を取得すると便利な場合があります。 呼び出し元情報属性は、このような情報を透過的に渡す方法を提供します。

省略可能なパラメーターにいずれかの呼び出し元情報属性が注釈付けされている場合、呼び出しで対応する引数を省略しても、既定のパラメーター値が置き換えられるとは限りません。 代わりに、呼び出し元のコンテキストに関する指定された情報が使用可能な場合、その情報は引数値として渡されます。

例:

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() を呼び出すと、呼び出しの行番号とファイル パス、および呼び出しが発生したメンバーの名前が出力されます。

end の例

呼び出し元情報属性は、デリゲート宣言を含め、任意の場所で省略可能なパラメーターで発生する可能性があります。 ただし、特定の呼び出し元情報属性には、属性として使用できるパラメーターの型に制限があるため、置換された値からパラメーター型への暗黙的な変換が常に存在します。

部分メソッド宣言の定義部分と実装部分の両方のパラメーターに対して同じ呼び出し元情報属性を持つことはエラーです。 定義パーツ内の呼び出し元情報属性のみが適用されますが、実装パーツでのみ発生する呼び出し元情報属性は無視されます。

呼び出し元の情報は、オーバーロードの解決には影響しません。 属性付き省略可能パラメーターは呼び出し元のソース コードから引き続き省略されるため、オーバーロードの解決では、省略された他の省略可能なパラメーター (§12.6.4) を無視するのと同じ方法でこれらのパラメーターが無視されます。

呼び出し元情報は、ソース コードで関数が明示的に呼び出された場合にのみ置き換わります。 暗黙的な親コンストラクター呼び出しなどの暗黙的な呼び出しにはソースの場所がないため、呼び出し元の情報は置き換えされません。 また、動的にバインドされた呼び出しは、呼び出し元情報に代わるものではありません。 このような場合に呼び出し元情報属性パラメーターを省略すると、パラメーターの指定された既定値が代わりに使用されます。

1 つの例外はクエリ式です。 これらは構文拡張と見なされ、呼び出し元が展開して、呼び出し元情報属性を持つ省略可能なパラメーターを省略すると、呼び出し元情報が置き換えられます。 使用される場所は、呼び出しが生成されたクエリ句の場所です。

指定されたパラメーターに複数の呼び出し元情報属性が指定されている場合は、 CallerLineNumberCallerFilePathCallerMemberNameの順序で認識されます。 次のパラメーター宣言について考えてみましょう。

[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 コード分析属性

22.5.7.1 全般

このセクションの属性は、null 許容診断と null 状態診断を提供するコンパイラをサポートするための追加情報を提供するために使用されます (§8.9.5)。 コンパイラは、null 状態の診断を実行する必要はありません。 これらの属性の有無は、言語やプログラムの動作には影響しません。 null 状態の診断を提供しないコンパイラは、これらの属性の存在を読み取り、無視する必要があります。 null 状態の診断を提供するコンパイラは、診断を通知するために使用するこれらの属性に対して、このセクションで定義されている意味を使用する必要があります。

コード分析属性は、名前空間 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 値に設定されている可能性があるため、コンパイラによって警告が生成される可能性があります。 属性が存在すると、その警告は抑制されます。 end の例

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 に設定してはならないことを呼び出し元に警告します。 end の例

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 許容の警告に影響を与えるためにのみ使用されます。 end の例

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!;
}

end の例

22.5.7.6 MaybeNull 属性

null 非許容の戻り値が null である可能性があることを指定します。

: 次の一般的なメソッドについて考えてみましょう。

#nullable enable
public T? Find<T>(IEnumerable<T> sequence, Func<T, bool> predicate) { ... }

このコードの考え方は、 Tstringに置き換えられた場合、 T? は null 許容注釈になるということです。 ただし、 T は参照型に制約されていないため、このコードは有効ではありません。 ただし、この属性を追加すると、問題が解決します。

#nullable enable
[return: MaybeNull]
public T Find<T>(IEnumerable<T> sequence, Func<T, bool> predicate) { ... }

この属性は、コントラクトが null 非許容型を意味することを呼び出し元に通知しますが、戻り値は実際には null可能性があります。 end の例

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 を呼び出してもかまいません。 end の例

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) { ... }

end の例

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) { ... }

end の例

22.6 相互運用のための属性

他の言語との相互運用のために、インデックス付きプロパティを使用してインデクサーを実装できます。 インデクサーに IndexerName 属性がない場合は、既定で Item 名が使用されます。 IndexerName属性を使用すると、開発者はこの既定値をオーバーライドし、別の名前を指定できます。

: 既定では、インデクサーの名前は Item。 これは、次のようにオーバーライドできます。

[System.Runtime.CompilerServices.IndexerName("TheItem")]
public int this[int index]
{
    get { ... }
    set { ... }
}

これで、インデクサーの名前が TheItem

end の例