カスタム属性の記述
独自のカスタム属性をデザインするために、いくつもの新しい概念をマスターする必要はありません。 オブジェクト指向プログラミングの経験があり、クラスのデザイン方法を知っている場合は、必要な知識のほとんどを既に持っています。 カスタム属性は、基本的には、System.Attribute から直接または間接的に派生する普通のクラスです。 普通のクラスと同様に、カスタム属性にはデータを格納したり取得したりするメソッドがあります。
カスタム属性クラスを正しくデザインするための主要な手順は次のとおりです。
AttributeUsageAttribute の適用
属性クラスの宣言
コンストラクターの宣言
プロパティの宣言
ここでは、上の各手順について説明し、最後にカスタム属性の例を示します。
AttributeUsageAttribute の適用
カスタム属性の宣言は、AttributeUsageAttribute から始まります。AttributeUsageAttribute は、属性クラスのいくつかの主要な特性を定義します。 たとえば、属性をほかのクラスで継承できるかどうかを指定したり、属性を適用できる要素を指定したりできます。 次のコードでは、AttributeUsageAttribute をどのように使用するかについて示します。
<AttributeUsage(AttributeTargets.All, Inherited := False, AllowMultiple := True)>
Public Class SomeClass
Inherits Attribute
'...
End Class
[AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)]
[AttributeUsage(AttributeTargets::All, Inherited = false, AllowMultiple = true)]
System.AttributeUsageAttribute には、カスタム属性の作成で重要な 3 つのメンバー、AttributeTargets、Inherited、および AllowMultiple があります。
AttributeTargets のメンバー
前の例で指定されている AttributeTargets.All は、この属性をすべてのプログラム要素に適用できることを示します。 これ以外に、属性を 1 つの特定のクラスにだけ適用できることを示す AttributeTargets.Class や、属性を 1 つの特定のメソッドにだけ適用できることを示す AttributeTargets.Method も指定できます。 この方法でカスタム属性を使用して、すべてのプログラム要素をマーキングできます。
また、AttributeTargets の複数のインスタンスを渡すこともできます。 カスタム属性を任意のクラスやメソッドに適用できるように指定するコード片を次に示します。
<AttributeUsage(AttributeTargets.Class Or AttributeTargets.Method)>
Public Class SomeOtherClass
Inherits Attribute
'...
End Class
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
[AttributeUsage(AttributeTargets::Class | AttributeTargets::Method)]
Inherited プロパティ
Inherited プロパティは、属性が適用されるクラスから継承されるクラスが、その属性を継承できるかどうかを指定します。 このプロパティには、true (既定値) または false のいずれかのフラグが指定できます。 たとえば次のコード例では、MyAttribute の Inherited の値は既定値の true ですが、YourAttribute の Inherited の値は false です。
' This defaults to Inherited = true.
Public Class MyAttribute
Inherits Attribute
'...
End Class
<AttributeUsage(AttributeTargets.Method, Inherited := False)>
Public Class YourAttribute
Inherits Attribute
'...
End Class
// This defaults to Inherited = true.
public class MyAttribute : Attribute
{
//...
}
[AttributeUsage(AttributeTargets.Method, Inherited = false)]
public class YourAttribute : Attribute
{
//...
}
// This defaults to Inherited = true.
public ref class MyAttribute : Attribute
{
//...
};
[AttributeUsage(AttributeTargets::Method, Inherited = false)]
public ref class YourAttribute : Attribute
{
//...
};
2 つの属性は、基本クラス MyClass 内のメソッドに次のように適用されます。
Public Class MeClass
<MyAttribute>
<YourAttribute>
Public Overridable Sub MyMethod()
'...
End Sub
End Class
public class MyClass
{
[MyAttribute]
[YourAttribute]
public virtual void MyMethod()
{
//...
}
}
public ref class MyClass
{
public:
[MyAttribute]
[YourAttribute]
virtual void MyMethod()
{
//...
}
};
最後に、基本クラス MyClass から YourClass クラスが継承されます。 メソッド MyMethod は、YourAttribute ではなく MyAttribute を示します。
Public Class YourClass
Inherits MeClass
' MyMethod will have MyAttribute but not YourAttribute.
Public Overrides Sub MyMethod()
'...
End Sub
End Class
public class YourClass : MyClass
{
// MyMethod will have MyAttribute but not YourAttribute.
public override void MyMethod()
{
//...
}
}
public ref class YourClass : MyClass
{
public:
// MyMethod will have MyAttribute but not YourAttribute.
virtual void MyMethod() override
{
//...
}
};
AllowMultiple プロパティ
AllowMultiple プロパティは、1 つの要素に対して属性の複数のインスタンスが存在できるかどうかを示します。 このプロパティを true に設定すると、複数のインスタンスが存在でき、false (既定値) に設定すると、1 つのインスタンスだけが存在できます。
MyAttribute の AllowMultiple の値が既定値の false、YourAttribute の値が true のコード例を次に示します。
' This defaults to AllowMultiple = false.
Public Class MyAttribute
Inherits Attribute
End Class
<AttributeUsage(AttributeTargets.Method, AllowMultiple := true)>
Public Class YourAttribute
Inherits Attribute
End Class
//This defaults to AllowMultiple = false.
public class MyAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class YourAttribute : Attribute
{
}
//This defaults to AllowMultiple = false.
public ref class MyAttribute : Attribute
{
};
[AttributeUsage(AttributeTargets::Method, AllowMultiple = true)]
public ref class YourAttribute : Attribute
{
};
これらの属性の複数のインスタンスが適用されたとき、MyAttribute ではコンパイル エラーが生成されます。 正しく使われている YourAttribute と、不適切に使われている MyAttribute の例を次に示します。
Public Class MyClass
' This produces an error.
' Duplicates are not allowed.
<MyAttribute>
<MyAttribute>
Public Sub MyMethod()
'...
End Sub
' This is valid.
<YourAttribute>
<YourAttribute>
Public Sub YourMethod()
'...
End Sub
End Class
public class MyClass
{
// This produces an error.
// Duplicates are not allowed.
[MyAttribute]
[MyAttribute]
public void MyMethod()
{
//...
}
// This is valid.
[YourAttribute]
[YourAttribute]
public void YourMethod()
{
//...
}
}
public ref class MyClass
{
public:
// This produces an error.
// Duplicates are not allowed.
[MyAttribute]
[MyAttribute]
void MyMethod()
{
//...
}
// This is valid.
[YourAttribute]
[YourAttribute]
void YourMethod()
{
//...
}
};
AllowMultiple プロパティと Inherited プロパティの両方が true に設定されている場合は、あるクラスから派生したクラスは属性を継承でき、同じ子クラスに同じ属性が適用される別のインスタンスを持つことができます。 AllowMultiple が false に設定されている場合は、親クラスでの属性の値が、子クラスにおける同じ属性の新しいインスタンスによって上書きされます。
属性クラスの宣言
AttributeUsageAttribute を適用した後で、属性の仕様を定義できます。 属性クラスの宣言は、次のコードに示すように、普通のクラスの宣言に似ています。
<AttributeUsage(AttributeTargets.Method)>
Public Class MyAttribute
Inherits Attribute
' . . .
End Class
[AttributeUsage(AttributeTargets.Method)]
public class MyAttribute : Attribute
{
// . . .
}
[AttributeUsage(AttributeTargets::Method)]
public ref class MyAttribute : Attribute
{
// . . .
};
この属性定義では、次の点が示されています。
属性クラスは、パブリック クラスとして宣言する必要があります。
規則では、属性クラスの名前の最後は Attribute にします。 この規則は必須ではありませんが、クラス名をわかりやすくするために、この規則に従うことをお勧めします。 属性を適用するとき、「Attribute」は省略できます。
すべての属性クラスは、System.Attribute から直接または間接的に継承する必要があります。
Microsoft Visual Basic では、すべてのカスタム属性クラスが AttributeUsageAttribute 属性を持つ必要があります。
コンストラクターの宣言
属性をコンストラクターで初期化する方法は、普通のクラスの場合と同じです。 次のコードは、一般的な属性のコンストラクターを示します。 このパブリック コンストラクターは 1 つのパラメーターをとり、メンバー変数と等しい値に設定します。
Public Sub New(myvalue As Boolean)
Me.myvalue = myvalue
End Sub
public MyAttribute(bool myvalue)
{
this.myvalue = myvalue;
}
MyAttribute(bool myvalue)
{
this->myvalue = myvalue;
}
コンストラクターをオーバーロードすることによって、ほかの値の組み合わせを使用できます。 カスタム属性クラスのプロパティも定義すると、属性を初期化するときに、名前付きパラメーターと位置指定パラメーターの組み合わせを使用できます。 通常は、すべての必須パラメーターを位置指定パラメーターとして定義し、すべてのオプション パラメーターを名前付きパラメーターとして定義します。 この場合は、必須パラメーターがないと属性を初期化できません。 ほかのすべてのパラメーターはオプションです。 Visual Basic では、属性クラスのコンストラクターには ParamArray 引数を使用できません。
次のコードでは、前のコンストラクターを使用する属性を必須パラメーターとオプション パラメーターを使用して適用する方法を示します。 このコード例では、属性が、必須パラメーターとして 1 つのブール値を、オプション パラメーターとして 1 つの文字列プロパティを持つことを想定しています。
' One required (positional) and one optional (named) parameter are applied.
<MyAttribute(false, OptionalParameter := "optional data")>
Public Class SomeClass
'...
End Class
' One required (positional) parameter is applied.
<MyAttribute(false)>
Public Class SomeOtherClass
'...
End Class
// One required (positional) and one optional (named) parameter are applied.
[MyAttribute(false, OptionalParameter = "optional data")]
public class SomeClass
{
//...
}
// One required (positional) parameter is applied.
[MyAttribute(false)]
public class SomeOtherClass
{
//...
}
// One required (positional) and one optional (named) parameter are applied.
[MyAttribute(false, OptionalParameter = "optional data")]
public ref class SomeClass
{
//...
};
// One required (positional) parameter is applied.
[MyAttribute(false)]
public ref class SomeOtherClass
{
//...
};
プロパティの宣言
名前付きパラメーターを定義する場合、または属性によって格納される値を簡単に返す手段を用意する場合は、プロパティを宣言します。 属性のプロパティは、返されるデータ型の記述を持つパブリック エンティティとして宣言する必要があります。 プロパティの値を保持する変数を定義してから、それに get メソッドと set メソッドを関連付けます。 次のコードは、属性に簡単なプロパティを実装する方法を示します。
Public Property MyProperty As Boolean
Get
Return Me.myvalue
End Get
Set
Me.myvalue = Value
End Set
End Property
public bool MyProperty
{
get {return this.myvalue;}
set {this.myvalue = value;}
}
property bool MyProperty
{
bool get() {return this->myvalue;}
void set(bool value) {this->myvalue = value;}
}
カスタム属性の例
ここでは、ここまでの説明のまとめとして、コードのセクションの作成者についての情報を提供する簡単な属性のデザイン方法を示します。 この例における属性は、プログラマの名前とレベル、およびコードがレビューされたかどうかの情報を格納します。 属性は、3 つのプライベート変数を使用して、保存する実効値を格納します。 各変数は、値を取得および設定するパブリック プロパティによって表されます。 最後に、2 つの必須パラメーターを持つコンストラクターが定義されます。
<AttributeUsage(AttributeTargets.All)>
Public Class DeveloperAttribute
Inherits Attribute
' Private fields.
Private myname As String
Private mylevel As String
Private myreviewed As Boolean
' This constructor defines two required parameters: name and level.
Public Sub New(name As String, level As String)
Me.myname = name
Me.mylevel = level
Me.myreviewed = False
End Sub
' Define Name property.
' This is a read-only attribute.
Public Overridable ReadOnly Property Name() As String
Get
Return myname
End Get
End Property
' Define Level property.
' This is a read-only attribute.
Public Overridable ReadOnly Property Level() As String
Get
Return mylevel
End Get
End Property
' Define Reviewed property.
' This is a read/write attribute.
Public Overridable Property Reviewed() As Boolean
Get
Return myreviewed
End Get
Set
myreviewed = value
End Set
End Property
End Class
[AttributeUsage(AttributeTargets.All)]
public class DeveloperAttribute : Attribute
{
// Private fields.
private string name;
private string level;
private bool reviewed;
// This constructor defines two required parameters: name and level.
public DeveloperAttribute(string name, string level)
{
this.name = name;
this.level = level;
this.reviewed = false;
}
// Define Name property.
// This is a read-only attribute.
public virtual string Name
{
get {return name;}
}
// Define Level property.
// This is a read-only attribute.
public virtual string Level
{
get {return level;}
}
// Define Reviewed property.
// This is a read/write attribute.
public virtual bool Reviewed
{
get {return reviewed;}
set {reviewed = value;}
}
}
[AttributeUsage(AttributeTargets::All)]
public ref class DeveloperAttribute : Attribute
{
// Private fields.
private:
String^ name;
String^ level;
bool reviewed;
public:
// This constructor defines two required parameters: name and level.
DeveloperAttribute(String^ name, String^ level)
{
this->name = name;
this->level = level;
this->reviewed = false;
}
// Define Name property.
// This is a read-only attribute.
virtual property String^ Name
{
String^ get() {return name;}
}
// Define Level property.
// This is a read-only attribute.
virtual property String^ Level
{
String^ get() {return level;}
}
// Define Reviewed property.
// This is a read/write attribute.
virtual property bool Reviewed
{
bool get() {return reviewed;}
void set(bool value) {reviewed = value;}
}
};
この属性は、フルネーム DeveloperAttribute または省略名 Developer を使用して、次のいずれかの方法で適用できます。
<Developer("Joan Smith", "1")>
-or-
<Developer("Joan Smith", "1", Reviewed := true)>
[Developer("Joan Smith", "1")]
-or-
[Developer("Joan Smith", "1", Reviewed = true)]
[Developer("Joan Smith", "1")]
-or-
[Developer("Joan Smith", "1", Reviewed = true)]
最初の例は、名前付き必須パラメーターだけを使用して適用される属性です。2 番目の例は、必須パラメーターとオプション パラメーターの両方を使用して適用される属性です。