C# コンパイラによって解釈されるその他の属性
コード内の要素にいくつかの属性を適用して、それらの要素にセマンティックな意味を追加できます。
Conditional
: メソッドの実行を、プリプロセッサ識別子に依存するようにします。Obsolete
: 将来削除する (可能性がある) ものとして型またはメンバーをマークします。AttributeUsage
: 属性を適用できる言語要素を宣言します。AsyncMethodBuilder
: 非同期メソッド ビルダー型を宣言します。InterpolatedStringHandler
: 既知のシナリオに対する補間文字列ビルダーを定義します。ModuleInitializer
: モジュールを初期化するメソッドを宣言します。SkipLocalsInit
: ローカル変数記憶域を 0 に初期化するコードを省略します。UnscopedRef
: 通常はscoped
として解釈されるref
変数をスコープなしとして扱う必要があることを宣言します。OverloadResolutionPriority
: あいまいなオーバーロードのオーバーロードの解決に影響を与えるタイブレーカー属性を追加します。Experimental
: 型またはメンバーを試験段階としてマークします。
コンパイラでは、これらのセマンティックの意味を使用して出力が変更され、コードを使用する開発者による間違いの可能性が報告されます。
Conditional
属性
Conditional
属性を使用すると、プリプロセス識別子に依存したメソッドの実行を指定できます。 Conditional
属性は ConditionalAttribute の別名であり、メソッドまたは属性クラスに適用できます。
次の例では、Conditional
は、プログラム固有の診断情報の表示を有効または無効にするメソッドに適用されています。
#define TRACE_ON
using System.Diagnostics;
namespace AttributeExamples;
public class Trace
{
[Conditional("TRACE_ON")]
public static void Msg(string msg)
{
Console.WriteLine(msg);
}
}
public class TraceExample
{
public static void Main()
{
Trace.Msg("Now in Main...");
Console.WriteLine("Done.");
}
}
TRACE_ON
識別子が定義されていない場合、トレース出力は表示されません。 対話型ウィンドウで自分で探してください。
多くの場合、Conditional
属性は、リリース ビルドではなく、デバッグ ビルドのトレース機能とログ機能を有効にするために DEBUG
識別子と共に使用されます。次に例を示します。
[Conditional("DEBUG")]
static void DebugMethod()
{
}
条件付きとマークされたメソッドが呼び出された場合、指定のプリプロセッサ シンボルの有無が、メソッドに対する呼び出しをコンパイラが含めるか省略するかを決定します。 シンボルが定義されている場合は呼び出しが実行され、定義されていない場合は省略されます。 条件付きメソッドは、クラスまたは構造体宣言のメソッドである必要があり、戻り値の型が void
である必要があります。 Conditional
を使用すると、#if…#endif
ブロック内にメソッドを含めるよりも、クリーンで洗練されており、エラーが発生しにくくなります。
メソッドに複数の Conditional
属性がある場合、1 つ以上の条件付きシンボルが定義されているときにコンパイラはメソッドに対する呼び出しを含めます (シンボルは OR 演算子で論理的にリンクされます)。 次の例では、A
または B
が存在すると、メソッドが呼び出されます。
[Conditional("A"), Conditional("B")]
static void DoIfAorB()
{
// ...
}
属性クラスでの Conditional
の使用
Conditional
属性は、属性クラスの定義にも適用できます。 次の例では、カスタム属性 Documentation
は、DEBUG
が定義されている場合、情報をメタデータに追加します。
[Conditional("DEBUG")]
public class DocumentationAttribute : System.Attribute
{
string text;
public DocumentationAttribute(string text)
{
this.text = text;
}
}
class SampleClass
{
// This attribute will only be included if DEBUG is defined.
[Documentation("This method displays an integer.")]
static void DoWork(int i)
{
System.Console.WriteLine(i.ToString());
}
}
Obsolete
属性
Obsolete
属性は、コード要素の使用が推奨されなくなったことを示します。 非推奨とマークされたエンティティを使用すると、警告またはエラーが生成されます。 Obsolete
属性は、1 回だけ使用できる属性であり、属性を使用できる任意のエンティティに適用できます。 Obsolete
は ObsoleteAttribute の別名です。
次の例では、Obsolete
属性がクラス A
およびメソッド B.OldMethod
に適用されています。 B.OldMethod
に適用された属性コンストラクターの 2 番目の引数が true
に設定されているため、このメソッドではコンパイル エラーが発生します。一方、クラス A
が使用されると、警告が生成されます。 しかし、B.NewMethod
の呼び出しでは、警告もエラーも生成されません。 たとえば、前の定義でこれを使用すると、次のコードで 2 つの警告と 1 つのエラーが生成されます。
namespace AttributeExamples
{
[Obsolete("use class B")]
public class A
{
public void Method() { }
}
public class B
{
[Obsolete("use NewMethod", true)]
public void OldMethod() { }
public void NewMethod() { }
}
public static class ObsoleteProgram
{
public static void Main()
{
// Generates 2 warnings:
A a = new A();
// Generate no errors or warnings:
B b = new B();
b.NewMethod();
// Generates an error, compilation fails.
// b.OldMethod();
}
}
}
最初の引数として属性コンストラクターに指定された文字列は、警告またはエラーの一部として表示されます。 クラス A
の警告が 2 つ生成されます。1 つはクラス参照の宣言の警告で、もう 1 つはクラス コンストラクターの警告です。 Obsolete
属性は引数なしで使用できますが、代わりに何を使用するかの説明を含めることをお勧めします。
C# 10 では、定数文字列補間と nameof
演算子を使用して、名前を一致させることができます。
public class B
{
[Obsolete($"use {nameof(NewMethod)} instead", true)]
public void OldMethod() { }
public void NewMethod() { }
}
Experimental
属性
C# 12 以降では、型、メソッド、アセンブリに試験的機能を示す System.Diagnostics.CodeAnalysis.ExperimentalAttribute マークを付けることができます。 ExperimentalAttribute の注釈が付いているメソッドまたは型にアクセスする場合、コンパイラから警告が出ます。 Experimental
属性でマークされたアセンブリまたはモジュールで宣言されているすべての型は試験的なものです。 これらのいずれかにアクセスすると、コンパイラによって警告が発行されます。 試験的機能を試験する目的でこのような警告を無効にできます。
警告
試験的な機能は変更される可能性があります。 API が変更されたり、将来の更新プログラムで削除されたりする可能性があります。 試験的機能を含めることは、将来の発展のためのアイディアやコンセプトに関するフィードバックをライブラリの作成者が得る方法になります。 試験段階としてマークされた機能を使用する場合は、細心の注意を払ってください。
Experimental
属性の詳細については、「機能仕様」を参照してください。
SetsRequiredMembers
属性
この SetsRequiredMembers
属性は、コンストラクターがそのクラスまたは構造体ですべての required
メンバーを設定することをコンパイラに通知します。 コンパイラは、System.Diagnostics.CodeAnalysis.SetsRequiredMembersAttribute 属性を持つ任意のコンストラクターがすべての required
メンバーを初期化することを前提としています。 このようなコンストラクターを呼び出すコードには、必要なメンバーを設定するためにオブジェクト初期化子は必要ありません。 SetsRequiredMembers
属性の追加は、位置指定レコードとプライマリ コンストラクターに主に役立ちます。
AttributeUsage
属性
AttributeUsage
属性によって、カスタム属性クラスの使用方法が決まります。 AttributeUsageAttribute は、カスタム属性定義に適用する属性です。 AttributeUsage
属性を使用すると、以下を制御できます。
- 属性を適用できるプログラム要素。 使用法を制限しない限り、属性は以下のプログラム要素のいずれかに適用できます。
- アセンブリ
- モジュール
- フィールド
- Event
- メソッド
- パラメーター
- プロパティ
- 戻り値
- Type
- 1 つのプログラム要素に属性を複数回適用できるかどうか。
- 派生クラスで属性を継承するかどうか。
明示的に適用すると、既定の設定は次の例のようになります。
[AttributeUsage(AttributeTargets.All,
AllowMultiple = false,
Inherited = true)]
class NewAttribute : Attribute { }
この例で NewAttribute
クラスはサポートされている任意のプログラム要素に適用できます。 ただし、各エンティティに適用できるのは 1 回のみです。 基底クラスに適用される属性が派生クラスで継承されます。
AllowMultiple 引数と Inherited 引数は省略できるので、次のコードは同じ効果を持ちます。
[AttributeUsage(AttributeTargets.All)]
class NewAttribute : Attribute { }
最初の AttributeUsageAttribute 引数は、AttributeTargets 列挙型の 1 つまたは複数の要素でなければなりません。 次の例のように、複数のターゲット型を OR 演算子で 1 つにまとめることができます。
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
class NewPropertyOrFieldAttribute : Attribute { }
プロパティ、または自動実装バッキング フィールドに属性を適用できるようになりました。 属性に field
指定子を指定しない限り、属性はプロパティに適用されます。 その両方の例を次に示します。
class MyClass
{
// Attribute attached to property:
[NewPropertyOrField]
public string Name { get; set; } = string.Empty;
// Attribute attached to backing field:
[field: NewPropertyOrField]
public string Description { get; set; } = string.Empty;
}
AllowMultiple 引数が true
の場合、次の例のように、結果の属性を 1 つのエンティティに複数回適用できます。
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
class MultiUse : Attribute { }
[MultiUse]
[MultiUse]
class Class1 { }
[MultiUse, MultiUse]
class Class2 { }
この例では、AllowMultiple
が true
に設定されているので、MultiUseAttribute
を繰り返し適用できます。 示されているどちらの形式でも、複数の属性を適用できます。
Inherited が false
の場合、派生クラスは属性付き基底クラスから属性を継承しません。 次に例を示します。
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
class NonInheritedAttribute : Attribute { }
[NonInherited]
class BClass { }
class DClass : BClass { }
この場合、NonInheritedAttribute
が継承によって DClass
に適用されることはありません。
これらのキーワードを使用して、属性を適用する場所を指定することもできます。 たとえば、指定子をfield:
使用して、自動的に実装されるプロパティのバッキング フィールドに属性を追加できます。 また、field:
、property:
、または param:
指定子を使用すれば、位置指定レコードから生成された要素のいずれかに属性を適用することができます。 例については、「プロパティ定義の位置指定構文」を参照してください。
AsyncMethodBuilder
属性
System.Runtime.CompilerServices.AsyncMethodBuilderAttribute 属性は、非同期の戻り値の型として可能な型に属性を追加します。 この属性は、指定した型が非同期メソッドから返されるときに、非同期メソッドの実装を構築する型を指定します。 AsyncMethodBuilder
属性は、次のような型に適用できます。
- アクセス可能な
GetAwaiter
メソッドがあります。 - System.Runtime.CompilerServices.ICriticalNotifyCompletion メソッドによって返されるオブジェクトによって、
GetAwaiter
インターフェイスが実装されます。
AsyncMethodBuilder
属性のコンストラクターが、関連付けられているビルダーの種類を指定します。 ビルダーは、次のアクセス可能なメンバーを実装する必要があります。
ビルダーの型を返す静的な
Create()
メソッド。非同期の戻り値の型を返す読み取り可能な
Task
プロパティ。タスクが失敗したときに例外を設定する
void SetException(Exception)
メソッド。タスクを完了としてマークし、必要に応じてタスクの結果を設定する
void SetResult()
またはvoid SetResult(T result)
メソッド次の API シグネチャを持つ
Start
メソッド。void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : System.Runtime.CompilerServices.IAsyncStateMachine
次のシグネチャを持つ
AwaitOnCompleted
メソッド。public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : System.Runtime.CompilerServices.INotifyCompletion where TStateMachine : System.Runtime.CompilerServices.IAsyncStateMachine
次のシグネチャを持つ
AwaitUnsafeOnCompleted
メソッド。public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : System.Runtime.CompilerServices.ICriticalNotifyCompletion where TStateMachine : System.Runtime.CompilerServices.IAsyncStateMachine
非同期メソッド ビルダーの詳細については、.NET に用意されている次のビルダーに関するページを参照してください。
- System.Runtime.CompilerServices.AsyncTaskMethodBuilder
- System.Runtime.CompilerServices.AsyncTaskMethodBuilder<TResult>
- System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder
- System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder<TResult>
C# 10 以降では、AsyncMethodBuilder
属性を非同期メソッドに適用して、その型のビルダーをオーバーライドできます。
InterpolatedStringHandler
属性および InterpolatedStringHandlerArguments
属性
C# 10 以降では、これらの属性を使用して型が "補間された文字列ハンドラー" であることを指定します。 .NET 6 ライブラリには、string
パラメーターの引数として補間された文字列を使用するシナリオ用に、System.Runtime.CompilerServices.DefaultInterpolatedStringHandler が既に含まれています。 補間された文字列の処理方法を制御したい他のケースがあるかもしれません。 ご自分のハンドラーを実装する型に System.Runtime.CompilerServices.InterpolatedStringHandlerAttribute を適用できます。 その型のコンストラクターのパラメーターに System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute を適用できます。
補間された文字列ハンドラーの作成について詳しくは、補間された文字列の改善のための C# 10 機能仕様を参照してください。
ModuleInitializer
属性
ModuleInitializer
属性は、アセンブリの読み込み時にランタイムが呼び出すメソッドをマークします。 ModuleInitializer
は ModuleInitializerAttribute の別名です。
ModuleInitializer
属性は、次のメソッドにのみ適用できます。
- 静的。
- パラメーターなし。
void
を返します。- 含まれているモジュール、つまり
internal
またはpublic
からアクセスできる。 - ジェネリック メソッドではない。
- ジェネリック クラスに含まれていない。
- ローカル関数ではない。
ModuleInitializer
属性は、複数のメソッドに適用できます。 その場合、ランタイムが呼び出す順序は決定的ですが、指定されていません。
次の例は、複数のモジュール初期化子メソッドの使用方法を示しています。 Init1
と Init2
のメソッドは、Main
の前に実行されます。各メソッドは Text
プロパティに文字列を追加します。 そのため Main
を実行するときには、Text
プロパティに、両方の初期化子メソッドの文字列が既に含まれています。
using System;
internal class ModuleInitializerExampleMain
{
public static void Main()
{
Console.WriteLine(ModuleInitializerExampleModule.Text);
//output: Hello from Init1! Hello from Init2!
}
}
using System.Runtime.CompilerServices;
internal class ModuleInitializerExampleModule
{
public static string? Text { get; set; }
[ModuleInitializer]
public static void Init1()
{
Text += "Hello from Init1! ";
}
[ModuleInitializer]
public static void Init2()
{
Text += "Hello from Init2! ";
}
}
ソース コード ジェネレーターでは、初期化コードの生成が必要になる場合があります。 そのコードを格納するための標準の場所は、モジュール初期化子が提供します。 その他のほとんどの場合は、モジュール初期化子ではなく、静的コンストラクターを記述する必要があります。
SkipLocalsInit
属性
SkipLocalsInit
属性は、メタデータへの出力時にコンパイラによって .locals init
フラグが設定されないようにします。 SkipLocalsInit
属性は、単一使用属性であり、メソッド、プロパティ、クラス、構造体、インターフェイス、またはモジュールに適用できますが、アセンブリには適用できません。 SkipLocalsInit
は SkipLocalsInitAttribute の別名です。
.locals init
フラグを使用すると、CLR によってメソッドで宣言されたすべてのローカル変数が既定値に初期化されます。 コンパイラでも、値を割り当てる前に変数が使用されないようにするため、.locals init
は通常は必要ありません。 ただし、stackalloc を使用してスタックに配列を割り当てる場合など、一部のシナリオでは、追加のゼロ初期化がパフォーマンスに重大な影響を与える可能性があります。 そのような場合は、SkipLocalsInit
属性を追加できます。 属性は、メソッドに直接適用すると、そのメソッドと、ラムダやローカル関数を含むすべての入れ子になった関数に影響します。 型またはモジュールに適用すると、内部で入れ子になっているすべてのメソッドに影響します。 この属性は抽象メソッドには影響しませんが、実装用に生成されたコードには影響します。
この属性には、AllowUnsafeBlocks コンパイラ オプションが必要です。 この要件により、場合によってはコードで未割り当てメモリを表示できる可能性があることが通知されます (たとえば、初期化されていないスタックに割り当てられたメモリからの読み取りなど)。
次の例は、stackalloc
を使用するメソッドに対する SkipLocalsInit
属性の効果を示しています。 このメソッドは、整数の配列が割り当てられたときに、メモリ内のものをすべて表示します。
[SkipLocalsInit]
static void ReadUninitializedMemory()
{
Span<int> numbers = stackalloc int[120];
for (int i = 0; i < 120; i++)
{
Console.WriteLine(numbers[i]);
}
}
// output depends on initial contents of memory, for example:
//0
//0
//0
//168
//0
//-1271631451
//32767
//38
//0
//0
//0
//38
// Remaining rows omitted for brevity.
このコードをご自分で試される場合は、 .csproj ファイルで AllowUnsafeBlocks
コンパイラ オプションを設定します。
<PropertyGroup>
...
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
UnscopedRef
属性
UnscopedRef
属性は、変数宣言をスコープなしとしてマークします。つまり、参照をエスケープできます。
コンパイラが ref
を暗黙的に scoped
として扱う場合に、この属性を追加します。
struct
インスタンス メソッドのthis
パラメーター。ref struct
型を参照するref
パラメーター。out
パラメーター。
System.Diagnostics.CodeAnalysis.UnscopedRefAttribute を適用すると、要素はスコープなしとしてマークされます。
OverloadResolutionPriority
属性
2 つのオーバーロードがあいまいになる可能性がある場合に、OverloadResolutionPriorityAttribute を使用すると、ライブラリの作成者は別のオーバーロードよりも 1 つのオーバーロードを優先できます。 その主なユース ケースは、ライブラリの作成者が、中断することなく既存のコードをサポートしながら、パフォーマンスの高いオーバーロードを記述することです。
たとえば、ReadOnlySpan<T> を使用してメモリ割り当てを減らす新しいオーバーロードを追加できます。
[OverloadResolutionPriority(1)]
public void M(params ReadOnlySpan<int> s) => Console.WriteLine("Span");
// Default overload resolution priority of 0
public void M(params int[] a) => Console.WriteLine("Array");
オーバーロードの解決では、2 つのメソッドが一部の引数型に対して同様に適切であると見なされます。 int[]
の引数の場合、最初のオーバーロードが優先されます。 コンパイラに ReadOnlySpan
バージョンを優先させるためには、そのオーバーロードの優先順位を上げることができます。 次の例は、属性を追加した場合の効果を示しています。
var d = new OverloadExample();
int[] arr = [1, 2, 3];
d.M(1, 2, 3, 4); // Prints "Span"
d.M(arr); // Prints "Span" when PriorityAttribute is applied
d.M([1, 2, 3, 4]); // Prints "Span"
d.M(1, 2, 3, 4); // Prints "Span"
優先度が最も高いオーバーロード優先度よりも低いオーバーロードはすべて、該当するメソッドのセットから削除されます。 この属性を持たないメソッドでは、オーバーロードの優先度が既定値の 0 に設定されています。 ライブラリ作成者は、新しいより優れたメソッド オーバーロードを追加するときに、この属性を最後の手段として使用する必要があります。 ライブラリ作成者は、オーバーロードの解決より適切な方法の選択に与える影響について深く理解している必要があります。 そうしないと、予期しないエラーが発生する場合があります。
関連項目
.NET