C#属性:メタデータでコードを拡張する完全ガイド
属性(Attribute)とは、プログラム内のクラス、メソッド、構造体、列挙子、アセンブリなどのさまざまな要素の動作についてランタイムに情報を伝えるための宣言的なタグです。角括弧([ ])で要素の上に配置することで、プログラムに宣言的な情報を追加できます。
属性は、コンパイラへの指示、コメント、説明、メソッド、クラスなどのメタデータをプログラムに追加するために使用されます。.NET Frameworkには2種類の属性があります:定義済み属性とカスタム属性です。
属性の指定構文
[attribute(positional_parameters, name_parameter = value, ...)] element
属性名とその値は、属性を適用する要素の前に角括弧内で指定します。位置指定パラメータは必須情報、名前付きパラメータはオプション情報を指定します。
定義済み属性
.NET Frameworkには3つの定義済み属性があります。
AttributeUsageConditionalObsolete
AttributeUsage属性
定義済み属性AttributeUsageは、カスタム属性クラスをどのように使用できるかを記述します。属性を適用できるアイテムの種類を指定します。
[AttributeUsage(
validon,
AllowMultiple = allowmultiple,
Inherited = inherited
)]
パラメータ:
validon- 属性を配置できる言語要素を指定。AttributeTargets列挙体の値の組み合わせ。デフォルトはAttributeTargets.Allallowmultiple- (省略可)AllowMultipleプロパティの値。trueなら複数回適用可能。デフォルトfalse(単一使用)inherited- (省略可)Inheritedプロパティの値。trueなら派生クラスに継承。デフォルトfalse(非継承)
AttributeUsageの例
[AttributeUsage(
AttributeTargets.Class
| AttributeTargets.Constructor
| AttributeTargets.Field
| AttributeTargets.Method
| AttributeTargets.Property,
AllowMultiple = true
)]
public class MyCustomAttribute : Attribute { }
Conditional属性
この定義済み属性は、指定されたプリプロセッサ識別子に依存して実行される条件付きメソッドをマークします。DebugやTraceなどの指定値に応じて、メソッド呼び出しの条件付きコンパイルを行います。例えば、コードのデバッグ中に変数の値を表示するのに使用します。
[Conditional("DEBUG")]
Conditional属性の例
#define DEBUG
using System;
using System.Diagnostics;
public class MyClass {
[Conditional("DEBUG")]
public static void Message(string msg) {
Console.WriteLine(msg);
}
}
class Test {
static void Function1() {
MyClass.Message("In Function 1.");
Function2();
}
static void Function2() {
MyClass.Message("In Function 2.");
}
public static void Main() {
MyClass.Message("In Main function.");
Function1();
Console.ReadKey();
}
}
実行結果(DEBUG定義時):
In Main function
In Function 1
In Function 2
#define DEBUGを削除またはコメントアウトすると、Messageメソッドへのすべての呼び出しがコンパイラによって無視され、出力は何もなくなります。メソッド自体も呼び出されません(ILに含まれない)。
Obsolete属性
この定義済み属性は、使用すべきでないプログラムエンティティをマークします。コンパイラに特定の対象要素を破棄するよう指示できます。例えば、クラスで新しいメソッドを使用するようになったが、古いメソッドをクラスに残しておきたい場合、古いメソッドをObsoleteとしてマークし、新しいメソッドを使用するようメッセージを表示できます。
構文
[Obsolete(message)]
[Obsolete(message, isError)]
message- 要素が古い理由と代わりに何を使うかを説明する文字列isError- trueならコンパイルエラー、falseなら警告(デフォルト)
Obsolete属性の例
using System;
public class MyClass {
[Obsolete("Don't use OldMethod, use NewMethod instead", true)]
static void OldMethod() {
Console.WriteLine("It is the old method");
}
static void NewMethod() {
Console.WriteLine("It is the new method");
}
public static void Main() {
OldMethod(); // コンパイルエラー: Don't use OldMethod, use NewMethod instead
}
}
カスタム属性の作成
.NET Frameworkでは、宣言的な情報を格納し実行時に取得できるカスタム属性を作成できます。この情報は設計基準とアプリケーションのニーズに応じて任意の対象要素に関連付けられます。
カスタム属性の作成と使用には4つのステップがあります。
- カスタム属性の宣言
- カスタム属性の構築
- ターゲットプログラム要素へのカスタム属性の適用
- リフレクションによる属性へのアクセス
最後のステップでは、メタデータを読み取ってさまざまな注釈を見つけるシンプルなプログラムを書きます。メタデータとはデータについてのデータ、または他のデータを記述するために使用される情報です。このプログラムは実行時に属性にアクセスするためにリフレクションを使用する必要があります。これは次章で説明します。
カスタム属性の宣言
新しいカスタム属性はSystem.Attributeクラスから派生させる必要があります。
[AttributeUsage(
AttributeTargets.Class
| AttributeTargets.Constructor
| AttributeTargets.Field
| AttributeTargets.Method
| AttributeTargets.Property,
AllowMultiple = true
)]
public class DeBugInfo : System.Attribute
カスタム属性の構築
DeBugInfoというカスタム属性を構築し、プログラムのデバッグで得られた情報を格納するとします。以下の情報を格納します。
- バグのコード番号
- バグを特定した開発者名
- コードの最終レビュー日
- 開発者の備考メッセージ
DeBugInfoクラスは最初の3つの情報用に3つのプライベートプロパティ、メッセージ用にパブリックプロパティを持ちます。したがって、バグ番号、開発者名、レビュー日は位置指定パラメータ、メッセージはオプション/名前付きパラメータとなります。
すべての属性には少なくとも1つのコンストラクタが必要です。位置指定パラメータはコンストラクタで渡します。
[AttributeUsage(
AttributeTargets.Class
| AttributeTargets.Constructor
| AttributeTargets.Field
| AttributeTargets.Method
| AttributeTargets.Property,
AllowMultiple = true
)]
public class DeBugInfo : System.Attribute {
private int bugNo;
private string developer;
private string lastReview;
public string message;
public DeBugInfo(int bg, string dev, string d) {
this.bugNo = bg;
this.developer = dev;
this.lastReview = d;
}
public int BugNo => bugNo;
public string Developer => developer;
public string LastReview => lastReview;
public string Message { get => message; set => message = value; }
}
カスタム属性の適用
属性は対象の直前に配置して適用します。
[DeBugInfo(45, "Zara Ali", "12/8/2012", Message = "Return type mismatch")]
[DeBugInfo(49, "Nuha Ali", "10/10/2012", Message = "Unused variable")]
class Rectangle {
protected double length;
protected double width;
public Rectangle(double l, double w) {
length = l; width = w;
}
[DeBugInfo(55, "Zara Ali", "19/10/2012", Message = "Return type mismatch")]
public double GetArea() => length * width;
[DeBugInfo(56, "Zara Ali", "19/10/2012")]
public void Display() {
Console.WriteLine("Length: {0}", length);
Console.WriteLine("Width: {0}", width);
Console.WriteLine("Area: {0}", GetArea());
}
}
よく使用される標準属性
| 属性 | 用途 |
|---|---|
[Serializable] | 型をシリアライズ可能にマーク |
[DataContract]/[DataMember] | WCF/シリアライゼーション用データ契約 |
[JsonPropertyName] | System.Text.Jsonでのプロパティ名指定 |
[Required]/[StringLength]等 | データ検証(DataAnnotations) |
[DllImport] | 非管理DLL関数のインポート |
[CallerMemberName]/[CallerFilePath]/[CallerLineNumber] | 呼び出し元情報の自動取得 |
[DoesNotReturn]/[NotNull]等 | 静的解析ヒント(C# 8.0+) |
ベストプラクティス
- カスタム属性クラス名は
Attributeサフィックスを付ける(DeBugInfoAttribute)。使用時はサフィックス省略可能 AttributeUsageで適用対象を明示的に制限する- 位置指定パラメータは必須項目、名前付きパラメータはオプション項目に使用
- 属性はメタデータのみ保持し、ロジックを含めない
- リフレクションで読み取る際はパフォーマンスに注意(キャッシュ推奨)
Obsoleteは削除予定APIに必ず付け、移行先を明記するConditional("DEBUG")はデバッグ専用ログ出力メソッドに使用し、リリースビルドから完全除去
C言語
- AgCu20 合金:23°C における包括的な機械的・熱的特性
- 合金 15CDV6(DIN 1.7734):高強度・高靭性、容易な溶接性
- DIN 1.7734 5 - 15CDV6合金:高強度、高靭性、容易な溶接性
- 15CDV6合金(DIN 1.7734) - 高強度、容易な溶接性、優れた靭性
- PtNi10 合金:包括的な機械的・熱的特性
- PtIr10 白金-イリジウム合金:高強度・高温材料
- Corroplastプレート – 精密機械加工向け高性能耐食鋼
- X46 AM FCE:重要パイプライン用途向け高性能API 5L鋼
- フェロ・チタニット® クロモニ – 高強度、非磁性チタン合金
- C#のヌル許容型:null値を安全に扱う完全ガイド
- Java 8 Optional完全ガイド:実践的なNull安全コードの書き方