次の方法で共有


22 属性

22.1 全般

C# 言語の多くを使用すると、プログラマーはプログラムで定義されているエンティティに関する宣言型情報を指定できます。 たとえば、クラス内のメソッドのアクセシビリティは、method_modifierpublicprotectedinternal および 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) があり、属性クラスで使用できるプログラム エンティティの種類を指定できます。

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

終了サンプル

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 です。

次のように、属性クラス XAttributeUsage 属性がアタッチされていない

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 属性パラメーターの型

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

  • 次のいずれかの種類: boolbytechardoublefloatintlongsbyteshortstringuintulongushort
  • 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 名は、eventfieldmethodparampropertyreturntype、および 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 から派生した型でない場合は、コンパイル時エラーが発生します。
  • それ以外の場合:
    • エラーが抑制されない限り、attribute_nametype_name (§7.8) として解決されます。 この解決が成功し、System.Attribute から派生した型になった場合、型はこの手順の結果になります。
    • 文字 Attribute は、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 {}

には、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 {}

終了サンプル

同じエンティティで 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 属性クラスである HelpStringClass1 の宣言で複数回使用しようとするため、コンパイル時エラーが発生します。

終了サンプル

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

終了サンプル

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

: 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 属性のコンパイル

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

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

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

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

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

§22.4.2 で定義されている用語を使用すると、属性インスタンスは、TCPN で示されます。EA に関連付けられた属性は、次の手順を実行すると、アセンブリから実行時に取得できます。

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

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

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.MALPHA の 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 を条件付きメソッドとして宣言します。 Class2Test メソッドは、このメソッドを呼び出します。 条件付きコンパイル シンボル DEBUG が定義されているため、Class2.Test が呼び出されると、Mが呼び出されます。 シンボル DEBUG が定義されていない場合、Class2.TestClass1.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 では定義されていないため、FClass2 の呼び出しは含まれますが、FClass3 の呼び出しは省略されます。

終了サンプル

継承チェーンで条件付きメソッドを使用すると、混乱を招く可能性があります。 形式 basebase.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 {}

クラス Class1Class2 はそれぞれ、属性 Test で修飾されます。これは、DEBUG が定義されているかどうかに基づいて条件付きになります。 このシンボルは、Class1 のコンテキストで定義されており、Class2 ではないため、Class1 のテスト属性の仕様が含まれる一方で、TestClass2 属性の仕様は省略されます。

終了サンプル

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 属性で修飾されます。 AMain を使用すると、"このクラスは廃止されました。代わりにクラス 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 属性が指定されている場合は、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 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) { ... }

このコードの考え方は、Tstring に置き換えられた場合、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 になります。

終了サンプル