2010-09-28T02:58:52の更新内容

programming/netfx2/overview/attribute/index.wiki.txt

current previous
1,1155 1,392
 
${smdncms:title,属性とメタデータ(Attribute)}
${smdncms:title,属性とメタデータ(Attribute)}
+
${smdncms:keywords,属性,カスタム属性,メタデータ,Attribute,AttributeUsage,AttributeTargets}
 
${smdncms:meta,toc-amazonlivelink-keyword,books-jp,.net framework}
${smdncms:meta,toc-amazonlivelink-keyword,books-jp,.net framework}
 
属性(Attribute)はデリゲートなどと同様、.NET Frameworkで採用されたプログラミングにおける新しい概念です。 といっても、デリゲートのように理解するのに多少の苦労を要するようなものではなく、属性は比較的簡単に理解することができると思います。 というのも、属性を指定する構文が単純で、ちょっとしたコメントをつけるのと同じ感覚で利用できるからです。
属性(Attribute)はデリゲートなどと同様、.NET Frameworkで採用されたプログラミングにおける新しい概念です。 といっても、デリゲートのように理解するのに多少の苦労を要するようなものではなく、属性は比較的簡単に理解することができると思います。 というのも、属性を指定する構文が単純で、ちょっとしたコメントをつけるのと同じ感覚で利用できるからです。
+

          
 
#googleadunit
#googleadunit
 

        

        
 
*属性の概要
*属性の概要
~
属性は様々な場所で、いろいろなプログラム要素に対して使用されます。 一般的に、いままで言語仕様の範囲を超えたオプションなどは、特別な構文やコンパイルオプションなどとして言語から切り離されていることがありました。 しかし、属性として存在する機能、例えば&msdn(netfx,type,System.SerializableAttribute){SerializableAttribute};, &msdn(netfx,type,System.ObsoleteAttribute){ObsoleteAttribute};などの機能は言語仕様の範囲を越えていますが、プログラムと非常に密接に関わり合っています。
属性は様々な場所で、いろいろなプログラム要素に対して使用されます。 一般的に、いままで言語仕様の範囲を超えたオプションなどは、特別な構文やコンパイルオプションなどとして言語から切り離されていることがありました。 しかし、属性として存在する機能、例えば SerializableAttribute, ObsoleteAttribute, DebuggerStepThroughAttributeなどの機能は言語仕様の範囲を越えていますが、プログラムと非常に密接に関わり合っています。
 

        

        
 
つまり、プログラムと深い関係がある情報(メタデータ)を、言語の機能を拡張することなく記述することができるのが属性です。 また、属性を採用することにより言語仕様の拡張をする必要がなくなるので、Visual C++とBorland C++などのように、拡張した機能によって言語間の互換性が失われると言う心配もありません。
つまり、プログラムと深い関係がある情報(メタデータ)を、言語の機能を拡張することなく記述することができるのが属性です。 また、属性を採用することにより言語仕様の拡張をする必要がなくなるので、Visual C++とBorland C++などのように、拡張した機能によって言語間の互換性が失われると言う心配もありません。
 

        

        
~
**.NET Frameworkで用意されている属性
*属性クラスの仕様
~
.NET Frameworkには様々な機能を持った属性が存在します。 その中でもよく使われるものに次の属性があります。 属性の具体的な使い道を理解するには以下のページが参考になるかもしれません。
.NET Frameworkには様々な機能を持った属性が存在します。 これだけでも十分と言えるのですが、何か独自の機能を追加するためには独自に属性を定義する必要があります。 しかし、独自に属性を定義すると言っても、実際に行うのは新たにクラス型を宣言するのと大差ありません。 というのは全ての属性は System.Attribute クラスの派生クラスであるからです。 実際にはそれだけではないので、独自の属性としてクラスを宣言するために必要な要件を次にまとめます。
~

          
+属性となるクラスはSystem.Attributeを継承している必要がある。
~
-[[programming/netfx2/overview/structlayout_fieldoffset]] - StructLayout属性、FieldOffset属性など
+属性となるクラスはpublicとして宣言されている必要がある。
~
-[[programming/netfx2/overview/debugging]] - Conditional属性、Obsolete属性など
+属性となるクラスの名前はAttributeで終わるべきである。 Attributeで終わる場合は、属性を適用する際にAttributeを省略することができる。
~
-[[programming/netfx2/overview/serialization]] - Serializable属性、NonSerialized属性など
+属性となるクラスは、その属性がどのプログラム要素に対して使用するものかを指定するため、AttributeUsage属性を適用することができる。 (ただし、VB.NETでは必須)
~
-[[programming/netfx2/overview/enumtype]] - Flags属性
+属性となるクラスは、最低でも一つのパブリックコンストラクタを持っている必要がある。
+
-[[programming/netfx2/overview/classlibrary]] - CLSCompliant属性
+
-[[programming/tips/extension_method_without_system_core_dll]] - Extension属性
+

          
+
*カスタム属性
+
ほとんどの場合、既に.NET Frameworkに存在する属性を使用するだけでも十分ですが、何か独自の機能を追加する場合やメタデータの埋め込みを行いたい場合も出てきます。 そういった場合は、独自の属性(カスタム属性)を定義する必要があります。 しかし、カスタム属性を定義すると言っても、実際に行う必要があるのは新たに属性を表すクラス型を宣言するだけです。 これといって難しいことはありません。
+

          
+
**カスタム属性の宣言
+
カスタム属性となるクラスには、いくつかの要件があります。 カスタム属性としてクラスを宣言するために必要な要件を次にまとめます。
+
+属性となるクラスは、&msdn(netfx,type,System.Attribute){Attributeクラス};を(直接または間接的に)継承している必要がある。 (Attributeクラスの派生クラスでなければならない)
+
+属性となるクラスは、publicとして宣言されている必要がある。
+
+属性となるクラスは、その属性がどのプログラム要素(クラス、メソッド、プロパティ等)に対して使用するものかを指定するため、&msdn(netfx,type,System.AttributeUsageAttribute){AttributeUsage属性};を適用することができる。 (ただし、VB.NETでは必須)
 
+属性のパラメータは、単純型、文字列型、オブジェクト型、列挙型、これらいずれかの一次元配列型、System.Typeの定数値でなければならない。
+属性のパラメータは、単純型、文字列型、オブジェクト型、列挙型、これらいずれかの一次元配列型、System.Typeの定数値でなければならない。
 

        

        
~
また、必須ではありませんが、属性となるクラスの名前はAttributeで終わるようにすべきです。 これは、クラスが属性であることがわかりやすくなる他、クラス名がAttributeで終わる場合、その属性を適用する際にAttributeを省略することができるようになるためです。
さらに、属性クラスの適用先などを指定するAttributeUsage属性のパラメータの詳細を次にまとめます。
-
:ValidOn|属性の適用対象を指定する。 アセンブリ、クラス、フィールド、メソッド、戻り値、パラメータなど。
-
:AllowMultiple|一つの要素に対して複数回同じ属性を指定することができるかを指定する。
-
:Inherited|その属性を適用されたクラスから派生したクラスが、その属性を継承できるかどうかを指定する。
 

        

        
~
早速、上記の要件を満たすクラスを宣言し、簡単なカスタム属性を作成してみます。
また、属性のパラメータの指定方法には二種類あり、一種類はコンストラクタの引数に対して使用する位置パラメータと、パブリックなフィールドに対して指定する名前付きパラメータです。 位置パラメータでは省略する事ができず、また、パラメータの位置によってその意味が変わります。 名前付きパラメータでは、省略が可能で、また位置も関係ありません。 次にその二種類のパラメータの例を示します。
 

        

        
~
#tabpage(C#)
#code(cs){{
~
#code(cs,カスタム属性クラスの宣言){{
// 位置パラメータを使用した例
~
[AttributeUsage(AttributeTargets.All)]
[AttributeUsage( AttributeTargets.All )]
~
public class CustomAttribute : Attribute
class UsingPositionalParameterAttribute : Attribute
 
{
{
 
}
}
+
}}
+
#tabpage(VB)
+
#code(vb,カスタム属性クラスの宣言){{
+
<AttributeUsage(AttributeTargets.All)> _
+
Public Class CustomAttribute
+
  Inherits Attribute
+
End Class
+
}}
+
#tabpage-end
+

          
+
ここでは、Attributeクラスを直接継承したカスタム属性CustomAttributeを宣言しています。 AttributeUsageとAttributeTargets.Allの詳細については[[後述>#AttributeUsage]]します。 このようにして定義したカスタム属性を使用するには、通常の属性と同じようにメソッドやクラスの前に付与するだけです。
 

        

        
~
#tabpage(C#)
// 位置パラメータと名前付きパラメータを使用した例
~
#code(cs,カスタム属性の適用){{
[AttributeUsage( AttributeTargets.All, AllowMultiple = true )]
~
using System;
class UsingNamedParameterAttribute : Attribute
+

          
+
[AttributeUsage(AttributeTargets.All)]
+
public class CustomAttribute : Attribute
 
{
{
 
}
}
+

          
+
[Custom]
+
class Sample {
+
  [CustomAttribute]
+
  public static void Main()
+
  {
+
    Console.WriteLine("Hello, world!");
+
  }
+
}
 
}}
}}
+
#tabpage(VB)
+
#code(vb,カスタム属性の適用){{
+
Imports System
+

          
+
<AttributeUsage(AttributeTargets.All)> _
+
Public Class CustomAttribute
+
  Inherits Attribute
+
End Class
+

          
+
<Custom> _
+
Class Sample
+
  <CustomAttribute> _
+
  Public Shared Sub Main()
+
    Console.WriteLine("Hello, world!")
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
この例にあるとおり、属性クラスの名前がAttributeで終わっている場合は、属性を適用する際に~Attributeを省略することができます。 もちろん、省略せずに完全な名前で適用することもできます。
 

        

        
~
この例のCustomAttributeでは、値や文字列などのパラメータを持っていないため属性が適用されているということ以外、何のメタデータも持っていません。 カスタム属性に具体的な意味を持たせるには、属性でパラメータを持たせるようにし、かつそのパラメータを取得できるようにする必要があります。
AttributeUsage属性では引数をとらないコンストラクタがなく、先ほど説明したValidOnはコンストラクタの引数、つまり位置パラメータとなっているので必ず指定する必要があります。 しかし、AllowMultipleはプロパティとして存在し、名前付きパラメータとして指定することができるので省略することができます。 名前付きパラメータは必ず名前を指定しなければなりません。
 

        

        
~
**カスタム属性のパラメータ
*独自の属性の宣言
~
カスタム属性にパラメータを持たせるには、引数のあるコンストラクタを用意するか、パブリックなフィールドを用意するか、二種類の方法があります。 これらの違いは後述するとし、まずは引数のあるコンストラクタをもつカスタム属性クラスを作成し、それを適用してみます。
これで独自の属性の宣言をする準備ができました。 そこで数種類の独自定義の属性クラスのサンプルを作ってみます。
 

        

        
~
#tabpage(C#)
#code(cs,独自に定義した属性と、その属性を適用した例){{
+
#code(cs){{
 
using System;
using System;
 

        

        
+
[AttributeUsage(AttributeTargets.All)]
+
public class CustomAttribute : Attribute
+
{
+
  public CustomAttribute(int intValue, string stringValue)
+
  {
+
  }
+
}
+

          
+
[Custom(16, "test")]
+
class Sample {
+
  [Custom(42, "sample")]
+
  public static void Main()
+
  {
+
    Console.WriteLine("Hello, world!");
+
  }
+
}
+
}}
+
#tabpage(VB)
+
#code(vb){{
+
Imports System
+

          
+
<AttributeUsage(AttributeTargets.All)> _
+
Public Class CustomAttribute
+
  Inherits Attribute
+

          
+
  Public Sub New(ByVal intValue As Integer, ByVal stringValue As String)
+
  End Sub
+
End Class
+

          
+
<Custom(16, "test")> _
+
Class Sample
+
  <Custom(42, "sample")> _
+
  Public Shared Sub Main()
+
    Console.WriteLine("Hello, world!")
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
このように、カスタム属性クラスに引数のあるコンストラクタを用意することで、属性を適用する際にパラメータを渡すことができます(実際には渡されたパラメータを保持し、取得することができないと意味がないのですが、その点については[[後述>#GetCustomAttributes]]します)。
 

        

        
+
先に書いたとおり、カスタム属性にパラメータを持たせるには二種類あり、コンストラクタの引数を用いた''位置パラメータ''と、もう一つパブリックなフィールドを用いた''名前付きパラメータ''です。 それぞれの違いは、
+
:位置パラメータ|省略できないパラメータで、かつ、パラメータの順序によってその意味が変わる
+
:名前付きパラメータ|省略が可能なパラメータで、また、パラメータの順序が変わっても意味は変わらない
 

        

        
~
となります。 上記の例で示した位置パラメータを、名前付きパラメータに置き換えると次のようになります。
// 全ての要素を対象とした属性
~

          
[AttributeUsage( AttributeTargets.All )]
~
#tabpage(C#)
class UseForAllTargetsAttribute : Attribute
+
#code(cs){{
+
using System;
+

          
+
[AttributeUsage(AttributeTargets.All)]
+
public class CustomAttribute : Attribute
 
{
{
+
  public int IntParam;
+
  public string StringParam;
 
}
}
 

        

        
~
[Custom(StringParam = "test", IntParam = 16)]
// メソッドに対して複数回指定できる属性
~
class Sample {
[AttributeUsage( AttributeTargets.Method, AllowMultiple = true )]
~
  [Custom(StringParam = "sample")]
class UseForMethodAllowMultipleAttribute : Attribute
+
  public static void Main()
+
  {
+
    Console.WriteLine("Hello, world!");
+
  }
+
}
+
}}
+
#tabpage(VB)
+
#code(vb){{
+
Imports System
+

          
+
<AttributeUsage(AttributeTargets.All)> _
+
Public Class CustomAttribute
+
  Inherits Attribute
+

          
+
  Public IntParam As Integer
+
  Public StringParam As String
+
End Class
+

          
+
<Custom(StringParam := "test", IntParam := 16)> _
+
Class Sample
+
  <Custom(StringParam := "sample")> _
+
  Public Shared Sub Main()
+
    Console.WriteLine("Hello, world!")
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
このように、名前付きパラメータでは、好きな順で指定することができ、必要なパラメータのみを指定することが出来ます。 もちろん、通常のクラスと同様に複数のコンストラクタを用意することもフィールドに初期値を設定することもでき、また位置パラメータと名前付きパラメータを同時に指定することも出来ます。 ただ、位置パラメータと名前付きパラメータを同時に指定する場合は、先に位置パラメータを指定し、後ろに名前付きパラメータを指定しなければなりません。
+

          
+
#tabpage(C#)
+
#code(cs){{
+
using System;
+

          
+
[AttributeUsage(AttributeTargets.All)]
+
public class CustomAttribute : Attribute
 
{
{
+
  public int IntParam = 42;
+
  public string StringParam = "default";
+

          
+
  public CustomAttribute(int x)
+
  {
+
  }
+

          
+
  public CustomAttribute(int x, int y)
+
  {
+
  }
 
}
}
 

        

        
~
[Custom(72, IntParam = 16)]
// 派生クラス・構造体に対して指定できる属性
~
class Sample {
[AttributeUsage( AttributeTargets.Struct | AttributeTargets.Class, Inherited = true )]
~
  [Custom(42, 16, StringParam = "sample")]
class UseForStructAndDerivedClassAttribute : Attribute
+
  // [Custom(StringParam = "sample", 42, 16)] <- 名前付きパラメータを先に指定することは出来ない
+
  public static void Main()
+
  {
+
    Console.WriteLine("Hello, world!");
+
  }
+
}
+
}}
+
#tabpage(VB)
+
#code(vb){{
+
Imports System
+

          
+
<AttributeUsage(AttributeTargets.All)> _
+
Public Class CustomAttribute
+
  Inherits Attribute
+

          
+
  Public IntParam As Integer = 42
+
  Public StringParam As String = "default"
+

          
+
  Public Sub New(ByVal x As Integer)
+
  End Sub
+

          
+
  Public Sub New(ByVal x As Integer, ByVal y As Integer)
+
  End Sub
+
End Class
+

          
+
<Custom(72, IntParam := 16)> _
+
Class Sample
+
  <Custom(42, 16, StringParam := "sample")> _
+
  ' <Custom(StringParam := "sample", 42, 16)> <- 名前付きパラメータを先に指定することは出来ない
+
  Public Shared Sub Main()
+
    Console.WriteLine("Hello, world!")
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
*&aname(GetCustomAttributes){属性の取得};
+
属性を適用できる対象には型・メソッド・アセンブリなどがありますが、そこから属性を取得するする方法は統一されています。 具体的には、&msdn(netfx,type,System.Type){Typeクラス};、&msdn(netfx,type,System.Reflection.MethodInfo){MethodInfoクラス};、&msdn(netfx,type,System.Reflection.Assembly){Assemblyクラス};などの&msdn(netfx,type,System.Reflection.ICustomAttributeProvider){ICustomAttributeProviderインターフェイス};を実装したクラスを使って、&msdn(netfx,method,System.Reflection.ICustomAttributeProvider.GetCustomAttributes){GetCustomAttributes()メソッド};を呼びします。
+

          
+
**型からの属性の取得
+
型から属性を取得するには、まず属性を取得したい型のTypeインスタンスを取得し、そこから取得したい属性のTypeを指定してGetCustomAttributes()メソッドを呼びします。
+

          
+
#tabpage(C#)
+
#code(cs){{
+
using System;
+

          
+
[AttributeUsage(AttributeTargets.All)]
+
public class CustomAttribute : Attribute
 
{
{
+
  public int Param = 0;
 
}
}
 

        

        
~
[Custom(Param = 16)]
// これらの属性を適用したクラス
~
class TestClass
[UseForAllTargetsAttribute, UseForStructAndDerivedClass]
-
class UsingAttributeClass
 
{
{
~
}
    [UseForMethodAllowMultiple, UseForMethodAllowMultiple]
~

          
    public void UsingAttributeMethod()
~
class Sample {
    {
+
  public static void Main()
+
  {
+
    Type t = typeof(TestClass);
+
    object[] attrs = t.GetCustomAttributes(typeof(CustomAttribute), false);
+

          
+
    if (0 < attrs.Length) {
+
      CustomAttribute c = attrs[0] as CustomAttribute;
+

          
+
      if (c != null) Console.WriteLine("Param = {0}", c.Param);
 
    }
    }
+
  }
 
}
}
+
}}
+
#tabpage(VB)
+
#code(vb){{
+
Imports System
+

          
+
<AttributeUsage(AttributeTargets.All)> _
+
Public Class CustomAttribute
+
  Inherits Attribute
+

          
+
  Public Param As Integer = 0
+
End Class
+

          
+
<Custom(Param := 16)> _
+
Class TestClass
+
End Class
+

          
+
Class Sample
+
  Public Shared Sub Main()
+
    Dim t As Type = GetType(TestClass)
+
    Dim attrs As Object() = t.GetCustomAttributes(GetType(CustomAttribute), False)
+

          
+
    If 0 < attrs.Length Then
+
      Dim c As CustomAttribute = DirectCast(attrs(0), CustomAttribute)
+

          
+
      If Not c Is Nothing Then Console.WriteLine("Param = {0}", c.Param)
+
    End If
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
 

        

        
+
GetCustomAttributes()メソッドはobjectの配列を返すので、まずは返された配列の長さをチェックします。 0より大きい場合は、実際に取得したい属性の型にキャストし、キャストできたら属性のパラメータの値を取得するという手順をとります。 これによって、型に適用された属性のパラメータの値を取得することが出来ます。
 

        

        
+
#prompt(実行結果){{
+
Param = 16
+
}}
 

        

        
+
なお、属性が適用されているかどうかだけを判断できればいい場合は、次の例のようにIsDefined()メソッドを使用することが出来ます。
 

        

        
+
#tabpage(C#)
+
#code(cs){{
+
using System;
 

        

        
+
[AttributeUsage(AttributeTargets.All)]
+
public class CustomAttribute : Attribute
+
{
+
}
 

        

        
~
// 属性を適用したクラス
// 位置パラメータとしてint型の値を三つとる属性  (AttributeUsage属性の適用を省略)
~
[Custom]
class ThreeArgumentsAttribute : Attribute
+
class Class1
 
{
{
-
    public ThreeArgumentsAttribute( int x, int y, int z)
-
    {
-
    }
 
}
}
 

        

        
~
// 属性を適用していないクラス
// ThreeArgumentsAttribute を適用したクラス
~
class Class2
[ThreeArguments( 1, 2, 3 )]
-
class UsingThreeArgumentClass
 
{
{
 
}
}
 

        

        
+
class Sample {
+
  public static void Main()
+
  {
+
    Console.WriteLine("Class1: {0}", typeof(Class1).IsDefined(typeof(CustomAttribute), false));
+
    Console.WriteLine("Class2: {0}", typeof(Class2).IsDefined(typeof(CustomAttribute), false));
+
  }
+
}
+
}}
+
#tabpage(VB)
+
#code(vb){{
+
Imports System
+

          
+
<AttributeUsage(AttributeTargets.All)> _
+
Public Class CustomAttribute
+
  Inherits Attribute
+
End Class
+

          
+
' 属性を適用したクラス
+
<Custom> _
+
Class Class1
+
End Class
+

          
+
' 属性を適用していないクラス
+
Class Class2
+
End Class
+

          
+
Class Sample
+
  Public Shared Sub Main()
+
    Console.WriteLine("Class1: {0}", GetType(Class1).IsDefined(GetType(CustomAttribute), false))
+
    Console.WriteLine("Class2: {0}", GetType(Class2).IsDefined(GetType(CustomAttribute), false))
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
Class1: True
+
Class2: False
+
}}
 

        

        
+
また、属性の型に関わらずすべての属性を取得する場合は、属性のTypeを指定せずにGetCustomAttributes()メソッドを呼びします。
+
#tabpage(C#)
+
#code(cs){{
+
using System;
 

        

        
+
[AttributeUsage(AttributeTargets.All)]
+
public class FooAttribute : Attribute
+
{
+
}
 

        

        
+
[AttributeUsage(AttributeTargets.All)]
+
public class BarAttribute : Attribute
+
{
+
}
 

        

        
~
[AttributeUsage(AttributeTargets.All)]
// 名前付きパラメータで指定する属性 (AttributeUsage属性の適用を省略)
~
public class BazAttribute : Attribute
class NamedParameterAttribute : Attribute
 
{
{
-
    public string StringValue = null;
-
    public int    IntValue    = 0;
-
    public Type   TypeValue   = null;
 
}
}
 

        

        
~
[Foo, Bar, Baz]
// NamedParameterAttribute を適用したクラス
~
class TestClass
[NamedParameter( StringValue = "String value", TypeValue = typeof( UsingNamedParameterClass ) )]
-
class UsingNamedParameterClass
 
{
{
 
}
}
+

          
+
class Sample {
+
  public static void Main()
+
  {
+
    Type t = typeof(TestClass);
+

          
+
    foreach (object attr in t.GetCustomAttributes(false)) {
+
      Console.WriteLine(attr.GetType().Name);
+
    }
+
  }
+
}
+
}}
+
#tabpage(VB)
+
#code(vb){{
+
Imports System
+

          
+
<AttributeUsage(AttributeTargets.All)> _
+
Public Class FooAttribute
+
  Inherits Attribute
+
End Class
+

          
+
<AttributeUsage(AttributeTargets.All)> _
+
Public Class BarAttribute
+
  Inherits Attribute
+
End Class
+

          
+
<AttributeUsage(AttributeTargets.All)> _
+
Public Class BazAttribute
+
  Inherits Attribute
+
End Class
+

          
+
<Foo, Bar, Baz> _
+
Class TestClass
+
End Class
+

          
+
Class Sample
+
  Public Shared Sub Main()
+
    Dim t As Type = GetType(TestClass)
+

          
+
    For Each attr As Object In t.GetCustomAttributes(false)
+
      Console.WriteLine(attr.GetType().Name)
+
    Next
+
  End Sub
+
End Class
 
}}
}}
+
#tabpage-end
 

        

        
~
#prompt(実行結果){{
この例では属性となるクラスの名前には全てAttributeをつけているので、どれが属性かはすぐわかると思います(そうでなくても、 Attributeを継承しているか否かでわかるはずです)。 まず、三つの属性クラスUseForAllTargetsAttribute、 UseForMethodAllowMultipleAttribute、UseForStructAndDerivedClassAttributeを宣言しています。 これらの詳細はコメントにあるとおりですが、実装は全くしていません。 また、これらの属性を適用したクラスが、 UsingAttributeClassです。
+
BazAttribute
+
BarAttribute
+
FooAttribute
+
}}
 

        

        
~
**メソッドからの属性の取得
このクラスを見てわかるとおり、属性を複数指定する場合には、「,(カンマ)」で区切ることで行えます。 また、かぎ括弧[]を複数回利用するという手段もとれます。 言うまでもありませんが、属性が対象としていない要素に属性を指定しようとするとコンパイルエラーになります。 さらに、この例ではその有効性を発揮していませんが、AllowMultipleにtrueを指定した属性は複数回指定することができます。
+
メソッドから属性を取得する場合は、対象となるメソッドの&msdn(netfx,type,System.Reflection.MethodInfo){MethodInfo};を取得しておく必要がありますが、それ以外は型から属性を取得する場合と何ら変わりありません。
 

        

        
~
#tabpage(C#)
続いて、38行目から50行目は位置パラメータを持つ属性とそれを適用したクラスの宣言です。 これも詳細な機能は実装はされていません。 位置パラメータではコンストラクタの引数として値を受け取るので、省略することができないので、全ての引数に対して値を設定する必要があります。 対して名前付きパラメータで指定する属性では省略することが可能です。 しかし、その代わりに指定するパラメータの名前を記述する必要があります。
+
#code(cs){{
+
using System;
+
using System.Reflection;
 

        

        
~
[AttributeUsage(AttributeTargets.All)]
56行目から68行目は名前付きパラメータを持つ属性とそれを適用したクラスの宣言ですが、この属性は、string、int、 Type型のパブリックメンバを持ちますが、コンストラクタは存在しません。 これらのメンバが名前付きパラメータとして値を指定できるメンバとなります。 65行目で実際にこの属性を適用していますが、IntValueのパラメータの指定を省略しています。 ここで、パラメータとなり得るメンバには、省略される可能性があるので必ず初期値を代入しておきます。
+
public class CustomAttribute : Attribute
+
{
+
  public int Param = 0;
+
}
 

        

        
~
class TestClass
最後に、今まで説明してきませんでしたが、任意の属性クラスから派生したクラスを適用することもできます(全ての属性はAttributeクラスから派生しているので言うまでもありませんが、要するに直接または間接のいずれの継承でかまわないということです)。
+
{
+
  [Custom(Param = 72)]
+
  public void TestMethod()
+
  {
+
  }
+
}
 

        

        
~
class Sample {
*アセンブリに適用された属性の取得
~
  public static void Main()
これまでは、独自の属性(カスタム属性)の宣言とその適用のサンプルを見てきたわけですが、属性自体にそのコードが実装されていませんでした。 また、属性にソースコードが記述されているからと言ってもそのまま属性が動作するわけではありません。 属性が特定の要素に対して適用されているかを調べ、適用されているならばその属性を取得し、適切なコードを実行する必要があります。 今までの例では、宣言されているだけで属性としての機能を果たしていません。
+
  {
+
    Type t = typeof(TestClass);
+
    MethodInfo m = t.GetMethod("TestMethod");
+
    object[] attrs = m.GetCustomAttributes(typeof(CustomAttribute), false);
 

        

        
~
    if (0 < attrs.Length) {
そこで、ここからは適用された属性を取得し、適切なコードを実行するための方法を考えていきます。 といっても、先程述べたとおり、実際には対象から属性を取得するだけなのでそれほど複雑なコーディングなどは必要ありません。 次に示すサンプルではカスタム属性 CopyrightAttributeを宣言し、アセンブリにその属性を適用、さらに現在実行中のアセンブリに適用されている全ての属性を列挙するという、属性に関する基本的な流れを記述しています。 今まで特に説明しませんでしたが、属性はアセンブリ自体にも指定できますが、その場合は [assembly: 属性名]と指定します。 ちなみに、VB.NETではモジュールに対しても属性を指定できます。
+
      CustomAttribute c = attrs[0] as CustomAttribute;
 

        

        
~
      if (c != null) Console.WriteLine("Param = {0}", c.Param);
#code(cs,アセンブリへの属性の適用と取得){{
~
    }
using Attributes;
+
  }
+
}
+
}}
+
#tabpage(VB)
+
#code(vb){{
+
Imports System
+
Imports System.Reflection
+

          
+
<AttributeUsage(AttributeTargets.All)> _
+
Public Class CustomAttribute
+
  Inherits Attribute
+

          
+
  Public Param As Integer = 0
+
End Class
+

          
+
Class TestClass
+
  <Custom(Param := 72)> _
+
  Public Sub TestMethod
+
  End Sub
+
End Class
+

          
+
Class Sample
+
  Public Shared Sub Main()
+
    Dim t As Type = GetType(TestClass)
+
    Dim m As MethodInfo = t.GetMethod("TestMethod")
+
    Dim attrs As Object() = m.GetCustomAttributes(GetType(CustomAttribute), False)
+

          
+
    If 0 < attrs.Length Then
+
      Dim c As CustomAttribute = DirectCast(attrs(0), CustomAttribute)
+

          
+
      If Not c Is Nothing Then Console.WriteLine("Param = {0}", c.Param)
+
    End If
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
Param = 72
+
}}
+

          
+
メソッド以外にも、コンストラクタやプロパティ、イベント、フィールドから属性を取得する場合も、これとほぼ同じ方法で取得することができます。
+

          
+
**アセンブリからの属性の取得
+
アセンブリから属性を取得する場合も、型やメソッドなどから取得する場合とほぼ同じです。 例として、現在実行中のアセンブリから属性を取得する場合は次のようになります。
+

          
+
#tabpage(C#)
+
#code(cs){{
 
using System;
using System;
 
using System.Reflection;
using System.Reflection;
 

        

        
~
// アセンブリに属性を適用
// アセンブリに対する属性の適用
~
[assembly: Custom(Param = 16)]
[assembly: Copyright( "santa marta", 2003 )]
 

        

        
~
[AttributeUsage(AttributeTargets.All)]
namespace Attributes
+
public class CustomAttribute : Attribute
 
{
{
~
  public int Param = 0;
    // カスタム属性
~
}
    [AttributeUsage(AttributeTargets.Assembly | 
-
                    AttributeTargets.Class |
-
                    AttributeTargets.Method,
-
                    Inherited = true)]
-
    public class CopyrightAttribute : Attribute
-
    {
-
        private string m_Author;
-
        private int    m_Year;
 

        

        
~
class Sample {
        public CopyrightAttribute( string author, int year )
~
  public static void Main()
        {
~
  {
            m_Author = author;
~
    // 現在実行しているアセンブリを取得
            m_Year   = year;
~
    Assembly a = Assembly.GetExecutingAssembly();
        }
~
    object[] attrs = a.GetCustomAttributes(typeof(CustomAttribute), false);

          
-
        public string Author
-
        {
-
            get { return m_Author; }
-
        }
-

          
-
        public int Year
-
        {
-
            get { return m_Year; }
-
        }
-

          
-
        public override string ToString()
-
        {
-
            return "CopyrightAttribute: Copyright(C) " + m_Year.ToString() + " " + m_Author;
-
        }
-
    }
 

        

        
~
    if (0 < attrs.Length) {
    // アプリケーションのエントリーポイントを提供するクラス
~
      CustomAttribute c = attrs[0] as CustomAttribute;
    class ApplicationEntrance
-
    {
-
        [STAThread]
-
        static void Main(string[] args)
-
        {
-

          
-
            // 現在実行中のアセンブリを取得する
-
            Assembly assm = Assembly.GetExecutingAssembly();
-

          
-
            // アセンブリに指定されている属性を取得する
-
            object[] attrs = assm.GetCustomAttributes( typeof( Attribute ), true );
-

          
-
            // 属性を列挙
-
            foreach ( Attribute attr in attrs )
-
            {
-
                Console.WriteLine( attr );
-
            }
 

        

        
~
      if (c != null) Console.WriteLine("Param = {0}", c.Param);
        }
 
    }
    }
~
  }

          
 
}
}
 
}}
}}
+
#tabpage(VB)
+
#code(vb){{
+
Imports System
+
Imports System.Reflection
+

          
+
' アセンブリに属性を適用
+
<Assembly: Custom(Param := 16)>
+

          
+
<AttributeUsage(AttributeTargets.All)> _
+
Public Class CustomAttribute
+
  Inherits Attribute
+

          
+
  Public Param As Integer = 0
+
End Class
+

          
+
Class Sample
+
  Public Shared Sub Main()
+
    ' 現在実行しているアセンブリを取得
+
    Dim a As Assembly = Assembly.GetExecutingAssembly()
+
    Dim attrs As Object() = a.GetCustomAttributes(GetType(CustomAttribute), False)
+

          
+
    If 0 < attrs.Length Then
+
      Dim c As CustomAttribute = DirectCast(attrs(0), CustomAttribute)
+

          
+
      If Not c Is Nothing Then Console.WriteLine("Param = {0}", c.Param)
+
    End If
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
 

        

        
 
#prompt(実行結果){{
#prompt(実行結果){{
~
Param = 16
System.Reflection.AssemblyCopyrightAttribute
-
System.Reflection.AssemblyProductAttribute
-
System.Diagnostics.DebuggableAttribute
-
System.Reflection.AssemblyKeyFileAttribute
-
System.Reflection.AssemblyDelaySignAttribute
-
System.Reflection.AssemblyTrademarkAttribute
-
System.Reflection.AssemblyConfigurationAttribute
-
CopyrightAttribute: Copyright(C) 2003 santa marta
-
System.Reflection.AssemblyCompanyAttribute
-
System.Reflection.AssemblyKeyNameAttribute
-
System.Reflection.AssemblyDescriptionAttribute
-
System.Reflection.AssemblyTitleAttribute
 
}}
}}
 

        

        
~
アセンブリに属性を適用する場合はコードの最初に記述する必要があるため、この例では属性クラスの宣言より前で属性の適用を行っています。 通常、アセンブリへの属性の適用はアセンブリ情報ファイル(AssebmlyInfo.csやAssebmlyInfo.vb)に記述します。
実行結果の検証は後にして、ひとまずコードの説明をしていきます。 属性の宣言自体は特に問題ないと思います。 この属性はアセンブリ、クラス、メソッドに対して使用し、その対象の著作権を記述するための属性です。 プロパティが読み取り専用になっているのは、コンストラクタで全ての値を指定させるためと、指定された後にその値を参照するためです。
 

        

        
~
この例では現在実行中のアセンブリから属性を取得していますが、ロード済みの他のアセンブリから取得する場合なども同じ方法で取得できます。
実際に属性を取得するコードは49行目から58行目に記述されています。 まず、現在実行中のアセンブリを取得します。 次にこのアセンブリから指定されている全ての属性を取得します。 それがGetCustomAttributes()メソッドです。 このメソッドは二つのバージョンがあり、今回は引数を二つとるバージョンを使用します。 一つ目の引数には取得したい属性の型情報を指定します。 二つ目の引数は一つ目の引数で Attributeクラスとその派生クラスを指定した場合には無視されます。 また、このメソッドの戻り値はobject型の配列となり、ここに適用されている属性の一覧が格納されます。 これでアセンブリから属性を取得することができます。
 

        

        
~
*&aname(AttributeUsage){属性の指定対象 (AttributeUsage属性)};
このサンプルではアセンブリに指定された属性を列挙していますが、アセンブリに対する属性の適用は6行目だけで記述されているはずなのに、実行結果を見るとこのアセンブリには複数の属性が指定されています。 というのも、アセンブリに対する属性の適用はこのファイルに記述されているコードでは6行目だけで適用していますが、このプロジェクトにはAssemblyInfo.csというアセンブリ情報ファイルが含まれていて、そこでも属性が指定されているためです。
+
&msdn(netfx,type,System.AttributeUsageAttribute){AttributeUsage属性};は属性の指定対象などを指定する属性です。 カスタム属性にこの属性を適用することで、カスタム属性の指定対象、継承時の動作などを定義することが出来ます。
 

        

        
~
**属性の指定対象 (ValidOn, AttributeTargets)
#code(cs,アセンブリ情報ファイル){{
~
AttributeUsage属性には省略できない位置パラメータとして、&msdn(netfx,member,System.AttributeUsageAttribute.ValidOn){ValidOnプロパティ};があります。 このパラメータは、属性の指定対象を&msdn(netfx,type,System.AttributeTargets){AttributeTargets列挙型};で指定します。 例えば、クラスのみに適用する属性、メソッドとプロパティのみに適用する属性、といった指定をするには、このパラメータで指定します。 ここまでの例ではAttributeTargets.Allを指定していましたが、これはどこにでも適用できる属性となります。
using System.Reflection;
-
using System.Runtime.CompilerServices;
 

        

        
~
次の例では、クラス型のみに適用できる属性ClassTypeAttributeと、メソッドとプロパティのみに適用できるMethodOrPropertyAttributeを宣言し、さまざまな箇所にしています。
//
-
// アセンブリに関する一般情報は以下の 
-
// 属性セットを通して制御されます。アセンブリに関連付けられている 
-
// 情報を変更するには、これらの属性値を変更してください。
-
//
-
[assembly: AssemblyTitle("")]
-
[assembly: AssemblyDescription("")]
-
[assembly: AssemblyConfiguration("")]
-
[assembly: AssemblyCompany("")]
-
[assembly: AssemblyProduct("")]
-
[assembly: AssemblyCopyright("")]
-
[assembly: AssemblyTrademark("")]
-
[assembly: AssemblyCulture("")]        
-

          
-
//
-
// アセンブリのバージョン情報は、以下の 4 つの属性で構成されます :
-
//
-
//      メジャー バージョン
-
//      マイナ バージョン 
-
//      ビルド番号
-
//      リビジョン
-
//
-
// 下にあるように、'*' を使って、すべての値を指定するか、 
-
// ビルドおよびリビジョン番号を既定値にすることができます :
-

          
-
[assembly: AssemblyVersion("1.0.*")]
-

          
-
//
-
// アセンブリに署名するには、使用するキーを指定しなければなりません。 
-
// アセンブリ署名に関する詳細については、Microsoft .NET Framework ドキュメントを参照してください。
-
//
-
// 下記の属性を使って、署名に使うキーを制御します。 
-
//
-
// メモ : 
-
//   (*) キーが指定されないと、アセンブリは署名されません。
-
//   (*) KeyName は、コンピュータにインストールされている
-
//        暗号サービス プロバイダ (CSP) のキーを表します。KeyFile は、
-
//       キーを含むファイルです。
-
//   (*) KeyFile および KeyName の値が共に指定されている場合は、 
-
//       以下の処理が行われます :
-
//       (1) KeyName が CSP に見つかった場合、そのキーが使われます。
-
//       (2) KeyName が存在せず、KeyFile が存在する場合、 
-
//           KeyFile にあるキーが CSP にインストールされ、使われます。
-
//   (*) KeyFile を作成するには、sn.exe (厳密な名前) ユーティリティを使ってください。
-
//       KeyFile を指定するとき、KeyFile の場所は、
-
//       プロジェクト出力 ディレクトリへの相対パスでなければなりません。
-
//       パスは、%Project Directory%\obj\<configuration> です。たとえば、KeyFile がプロジェクト ディレクトリにある場合、
-
//       AssemblyKeyFile 属性を 
-
//       [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] として指定します。
-
//   (*) 遅延署名は高度なオプションです。
-
//       詳細については Microsoft .NET Framework ドキュメントを参照してください。
-
//
-
[assembly: AssemblyDelaySign(false)]
-
[assembly: AssemblyKeyFile("")]
-
[assembly: AssemblyKeyName("")]
-
}}
-

          
-
このコードを見てわかるとおり、全てのコードが属性からなっています。 これらの属性は全てアセンブリに対して適用されているので、アセンブリに指定されている属性を取得しようとするとこれらの属性も取得されるわけです。
 

        

        
~
#tabpage(C#)
ところで、これらのバージョン情報など情報は、Visual Basic では実行可能ファイルを生成する際のプロパティとして、Visual C++ ではリソースとして埋め込まれていました。 しかし、このアセンブリ情報ファイルのように.NET Frameworkでは同様のことを属性によって実現することができます。 メタデータを属性として任意の場所で記述できることが、属性の特徴であると言えるのではないでしょうか。
+
#code(cs){{
+
using System;
 

        

        
~
// クラス型のみに適用できる属性
*型に適用された属性の取得
~
[AttributeUsage(AttributeTargets.Class)]
アセンブリ以外からの属性の取得も同様の方法で行えます。 実際にはICustomAttributeProviderインターフェイスを実装したクラスのGetCustomAttributes()を呼び出せば取得できます。 といっても実際のサンプルをみてみないと説明にならないので、先ほどの属性をクラスとそのメソッドに適用し、それらを取得するコードを次に示します。
+
public class ClassTypeAttribute : Attribute
+
{
+
}
 

        

        
~
// メソッドとプロパティのみに適用できる属性
#code(cs,型からの属性の取得){{
~
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property)]
using System;
~
public class MethodOrPropertyAttribute : Attribute
using System.Diagnostics;
~
{
using System.Reflection;
+
}
 

        

        
~
[ClassType] // <- クラス型に属性を適用
namespace Attributes
+
class TestClass
 
{
{
~
  // [MethodOrProperty] <-AttributeTargets.Method or AttributeTargets.Propertyなのでコンストラクタには適用できない
    // 属性を適用されたクラス
~
  TestClass()
    [Copyright( "santa marta", 2003 ), DebuggerStepThrough()]
~
  {
    class UsingAttributeClass
+
  }
+

          
+
  [MethodOrProperty] // <- メソッドに属性を適用
+
  void TestMethod()
+
  {
+
  }
+
}
+

          
+
// [ClassType] <- AttributeTargets.Classなので構造体には適用できない
+
struct TestStructure
+
{
+
  [MethodOrProperty] // <- プロパティに属性を適用
+
  int TestProperty
+
  {
+
    get
 
    {
    {
~
      return 0;
        [Copyright( "Some programmer", 2003 )]
-
        public int ReturnsIntMethod()
-
        {
-
            return 0;
-
        }
-

          
-
        [Copyright( "Old programmer", 1999 )]
-
        public string ReturnsStringMethod()
-
        {
-
            return "";
-
        }
-

          
-
        [Copyright( "Another programmer", 2001 )]
-
        public void ReturnsVoidMethod()
-
        {
-
        }
 
    }
    }
+
  }
+
}
+
}}
+
#tabpage(VB)
+
#code(vb){{
+
Imports System
+

          
+
' クラス型のみに適用できる属性
+
<AttributeUsage(AttributeTargets.Class)> _
+
Public Class ClassTypeAttribute
+
  Inherits Attribute
+
End Class
+

          
+
' メソッドとプロパティのみに適用できる属性
+
<AttributeUsage(AttributeTargets.Method Or AttributeTargets.Property)> _
+
Public Class MethodOrPropertyAttribute
+
  Inherits Attribute
+
End Class
+

          
+
<ClassType> _ ' <- クラス型に属性を適用
+
Class TestClass
+
  ' <MethodOrProperty> _ <-AttributeTargets.Method or AttributeTargets.Propertyなのでコンストラクタには適用できない
+
  Sub New()
+
  End Sub
+

          
+
  <MethodOrProperty> _ ' <- メソッドに属性を適用
+
  Sub TestMethod()
+
  End Sub
+
End Class
+

          
+
' <ClassType> _ <- AttributeTargets.Classなので構造体には適用できない
+
Structure TestStructure
+
  <MethodOrProperty> _ ' <- プロパティに属性を適用
+
  ReadOnly Property TestProperty() As Integer
+
    Get
+
      Return 0
+
    End Get
+
  End Property
+
End Structure
+
}}
+
#tabpage-end
 

        

        
~
属性の適用対象はコンパイル時にチェックされます。 コメントアウトしてある箇所を有効にすると、コンパイルエラーが発生するようになります。
    // アプリケーションのエントリーポイントを提供するクラス
~

          
    class ApplicationEntrance
~
ValidOnプロパティには、次の値を組み合わせて指定することができます。
    {
~
|*ValidOnプロパティに指定できる値と属性の対象
        [STAThread]
~
|~値|~対象|h
        static void Main(string[] args)
~
|>|~型|
        {
~
|AttributeTargets.Class|クラス型|
            // どこかでインスタンスが作成されたとする
~
|AttributeTargets.Struct|構造体型|
            UsingAttributeClass inst = new UsingAttributeClass();
~
|AttributeTargets.Interface|インターフェイス型|

          
~
|AttributeTargets.Enum|列挙型|
            // そのインスタンスの型情報を取得する
~
|AttributeTargets.Delegate|デリゲート型|
            Type t = inst.GetType();
~
|AttributeTargets.GenericParameter|ジェネリック型の型パラメータ|

          
~
|>|~型のメンバ|
            // 型に指定されているCopyrightAttribute属性を取得する
~
|AttributeTargets.Method|メソッド|
            object[] attrs = t.GetCustomAttributes( typeof( CopyrightAttribute ), true );
~
|AttributeTargets.Property|プロパティ|

          
~
|AttributeTargets.Constructor|コンストラクタ|
            // 列挙する
~
|AttributeTargets.Field|フィールド|
            Console.WriteLine( t.Name + " に指定されている属性" );
~
|AttributeTargets.Event|イベント|

          
~
|AttributeTargets.Parameter|メソッド・コンストラクタ等の引数|
            foreach ( CopyrightAttribute attr in attrs )
~
|AttributeTargets.ReturnValue|メソッド・プロパティ等の戻り値|
            {
~
|>|~その他|
                Console.WriteLine( attr );
~
|AttributeTargets.Assembly|アセンブリ|
            }
~
|AttributeTargets.Module|モジュール (VB.NETのモジュールではなくアセンブリ内のモジュール)|

          
~
|AttributeTargets.All|すべて (上記のうち任意の要素、ValidOnプロパティのデフォルト値)|
            Console.WriteLine();
~

          

          
~
**複数指定の許可 (AllowMultiple)

          
~
AttributeUsage属性の&msdn(netfx,member,System.AttributeUsageAttribute.AllowMultiple){AllowMultipleフィールド};は省略可能な名前付きパラメータで、属性を2つ以上適用できるかどうかを指定します。 指定しなかった場合のデフォルトはfalse(1つしか適用できない)です。
            // 型情報からその型のメソッド情報を取得する
~

          
            MethodInfo[] methods = t.GetMethods();
~
次の例では、OriginalAuthorAttributeは1つのみ適用できる属性、ModifierAttributeは複数個適用できる属性として宣言しています。

          
~

          
            // 全てのメソッドに対して
~
#tabpage(C#)
            foreach ( MethodInfo method in methods )
~
#code(cs){{
            {
~
using System;
                // 属性を取得
~
using System.Reflection;
                attrs = method.GetCustomAttributes( typeof( CopyrightAttribute ), true );
~

          

          
~
// 1つだけ指定できる属性
                if ( 0 < attrs.Length )
~
[AttributeUsage(AttributeTargets.Method)]
                {
~
public class OriginalAuthorAttribute : Attribute
                    // 列挙する
~
{
                    Console.WriteLine( t.Name +"." + method.Name + "() に指定されている属性" );
~
  public string Name {

          
~
    get; private set;
                    foreach ( CopyrightAttribute attr in attrs )
~
  }
                    {
~

          
                        Console.WriteLine( "\t" + attr );
~
  public OriginalAuthorAttribute(string name)
                    }
~
  {
                }
~
    Name = name;
                else
~
  }
                {
~
}
                    Console.WriteLine( t.Name +"." + method.Name + "() には属性は指定されていません" );
~

          
                }
~
// 複数個指定できる属性
            }
~
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
        }
+
public class ModifierAttribute : Attribute
+
{
+
  public string Name {
+
    get; private set;
+
  }
+

          
+
  public ModifierAttribute(string name)
+
  {
+
    Name = name;
+
  }
+
}
+

          
+
class TestClass
+
{
+
  [OriginalAuthor("Alice")]
+
  // [OriginalAuthor("Eve")] <- AllowMultiple = falseなので複数個指定できない
+
  [Modifier("Bob")]
+
  [Modifier("Carol")]
+
  [Modifier("Dave"), Modifier("Eve")]
+
  public void TestMethod()
+
  {
+
  }
+
}
+

          
+
class Sample {
+
  public static void Main()
+
  {
+
    MethodInfo m = typeof(TestClass).GetMethod("TestMethod");
+

          
+
    foreach (object attr in m.GetCustomAttributes(false)) {
+
      if (attr is OriginalAuthorAttribute) {
+
        Console.WriteLine("Original Author: {0}", (attr as OriginalAuthorAttribute).Name);
+
      }
+
      else if (attr is ModifierAttribute) {
+
        Console.WriteLine("Modifier: {0}", (attr as ModifierAttribute).Name);
+
      }
 
    }
    }
+
  }
 
}
}
 
}}
}}
+
#tabpage(VB)
+
#code(vb){{
+
Imports System
+
Imports System.Reflection
+

          
+
' 1つだけ指定できる属性
+
<AttributeUsage(AttributeTargets.Method)> _
+
Public Class OriginalAuthorAttribute
+
  Inherits Attribute
+

          
+
  Private _name As String
+

          
+
  Public ReadOnly Property Name As String
+
    Get
+
      Return _name
+
    End Get
+
  End Property
+

          
+
  Public Sub New(ByVal name As String)
+
    _name = name
+
  End Sub
+
End Class
+

          
+
' 複数個指定できる属性
+
<AttributeUsage(AttributeTargets.Method, AllowMultiple := True)> _
+
Public Class ModifierAttribute
+
  Inherits Attribute
+

          
+
  Private _name As String
+

          
+
  Public ReadOnly Property Name As String
+
    Get
+
      Return _name
+
    End Get
+
  End Property
+

          
+
  Public Sub New(ByVal name As String)
+
    _name = name
+
  End Sub
+
End Class
+

          
+
Class TestClass
+
  ' <OriginalAuthor("Eve")> _ <- AllowMultiple = falseなので複数個指定できない
+
  <OriginalAuthor("Alice")> _
+
  <Modifier("Bob")> _
+
  <Modifier("Carol")> _
+
  <Modifier("Dave"), Modifier("Eve")> _
+
  Public Sub TestMethod()
+
  End Sub
+
End Class
+

          
+
Class Sample
+
  Public Shared Sub Main()
+
    Dim m As MethodInfo = GetType(TestClass).GetMethod("TestMethod")
+

          
+
    For Each attr As Object In m.GetCustomAttributes(false)
+
      If TypeOf attr Is OriginalAuthorAttribute Then
+
        Console.WriteLine("Original Author: {0}", DirectCast(attr, OriginalAuthorAttribute).Name)
+
      Else If TypeOf attr Is ModifierAttribute Then
+
        Console.WriteLine("Modifier: {0}", DirectCast(attr, ModifierAttribute).Name)
+
      End if
+
    Next
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
 

        

        
 
#prompt(実行結果){{
#prompt(実行結果){{
~
Modifier: Eve
UsingAttributeClass に指定されている属性
~
Modifier: Dave
CopyrightAttribute: Copyright(C) 2003 santa marta
+
Modifier: Carol
+
Modifier: Bob
+
Original Author: Alice
+
}}
 

        

        
~
ValidOn同様、属性の適用回数はコンパイル時にチェックされます。 コメントアウトしてある部分を有効にしてOriginalAuthorAttributeを複数個適用しようとすると、コンパイルエラーとなります。
UsingAttributeClass.GetHashCode() には属性は指定されていません
~

          
UsingAttributeClass.Equals() には属性は指定されていません
~
上記の例にあるように、属性を複数個指定する場合は、個別に括弧でくくる方法と、一つの各個の中に複数の属性を指定する方法の二つがあります。 意味は同じなので、読みやすさなどを考慮し、より適切な記述を選ぶことができます。 また、属性の取得方法は、一つの場合でも複数個の場合でも変わりませんが、実行結果からも分かるように、属性の指定順と取得した結果は必ずしも一致するとは限りません。
UsingAttributeClass.ToString() には属性は指定されていません
~

          
UsingAttributeClass.ReturnsIntMethod() に指定されている属性
~
**属性の継承 (Inherited)
        CopyrightAttribute: Copyright(C) 2003 Some programmer
~
AttributeUsage属性の&msdn(netfx,member,System.AttributeUsageAttribute.Inherited){Inheritedフィールド};は省略可能な名前付きパラメータで、クラスを継承した場合に、基底クラスに適用されている属性を継承するかどうかを指定します。 指定しなかった場合のデフォルトはtrue(継承する)です。
UsingAttributeClass.ReturnsStringMethod() に指定されている属性
~

          
        CopyrightAttribute: Copyright(C) 1999 Old programmer
~
このパラメータはGetCustomAttributes()メソッド等で属性を取得しようとした場合の動作に影響します。 GetCustomAttributes()等のメソッドでは、引数に継承した属性も含むかどうかを指定することができ、この引数の値と属性のInheritedの設定により、戻り値に含まれるかどうかが変わってきます。
UsingAttributeClass.ReturnsVoidMethod() に指定されている属性
~

          
        CopyrightAttribute: Copyright(C) 2001 Another programmer
~
次の例では、継承される属性InheritedAttributeと継承されない属性NotInheritedAttributeを宣言し、基底クラスBaseClassに適用しています。 BaseClassと、BaseClassを継承したDerivedClassのそれぞれの型に対してGetCustomAttributes()メソッドを呼び出し、結果の違いを見てみます。
UsingAttributeClass.GetType() には属性は指定されていません
~

          
Press any key to continue
+
#tabpage(C#)
+
#code(cs){{
+
using System;
+

          
+
// 継承される属性
+
[AttributeUsage(AttributeTargets.Class, Inherited = true)]
+
public class InheritedAttribute : Attribute
+
{
+
}
+

          
+
// 継承されない属性
+
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
+
public class NotInheritedAttribute : Attribute
+
{
+
}
+

          
+
// 基底クラス
+
[Inherited, NotInherited]
+
class BaseClass
+
{
+
}
+

          
+
[AttributeUsage(AttributeTargets.Class)]
+
public class CustomAttribute : Attribute
+
{
+
}
+

          
+
// 派生クラス
+
[Custom]
+
class DerivedClass : BaseClass
+
{
+
}
+

          
+
class Sample {
+
  public static void Main()
+
  {
+
    // 基底クラスのすべての属性を取得
+
    Console.WriteLine("BaseClass");
+

          
+
    foreach (object attr in typeof(BaseClass).GetCustomAttributes(false)) {
+
      Console.WriteLine("  {0}", attr.GetType().Name);
+
    }
+

          
+
    // 派生クラスのすべての属性を取得 (継承した属性は含めない)
+
    Console.WriteLine("DerivedClass (inherited = false)");
+

          
+
    foreach (object attr in typeof(DerivedClass).GetCustomAttributes(false)) {
+
      Console.WriteLine("  {0}", attr.GetType().Name);
+
    }
+

          
+
    // 派生クラスのすべての属性を取得 (継承した属性も含める)
+
    Console.WriteLine("DerivedClass (inherited = true)");
+

          
+
    foreach (object attr in typeof(DerivedClass).GetCustomAttributes(true)) {
+
      Console.WriteLine("  {0}", attr.GetType().Name);
+
    }
+
  }
+
}
+
}}
+
#tabpage(VB)
+
#code(vb){{
+
Imports System
+

          
+
' 継承される属性
+
<AttributeUsage(AttributeTargets.Class, Inherited := True)> _
+
Public Class InheritedAttribute
+
  Inherits Attribute
+
End Class
+

          
+
' 継承されない属性
+
<AttributeUsage(AttributeTargets.Class, Inherited := False)> _
+
Public Class NotInheritedAttribute
+
  Inherits Attribute
+
End Class
+

          
+
' 基底クラス
+
<Inherited, NotInherited> _
+
Class BaseClass
+
End Class
+

          
+
<AttributeUsage(AttributeTargets.Class)> _
+
Public Class CustomAttribute
+
  Inherits Attribute
+
End Class
+

          
+
' 派生クラス
+
<Custom> _
+
Class DerivedClass
+
  Inherits BaseClass
+
End Class
+

          
+
Class Sample
+
  Public Shared Sub Main()
+
    ' 基底クラスのすべての属性を取得
+
    Console.WriteLine("BaseClass")
+

          
+
    For Each attr As Object In GetType(BaseClass).GetCustomAttributes(false)
+
      Console.WriteLine("  {0}", attr.GetType().Name)
+
    Next
+

          
+
    ' 派生クラスのすべての属性を取得 (継承した属性は含めない)
+
    Console.WriteLine("DerivedClass (inherited = false)")
+

          
+
    For Each attr As Object In GetType(DerivedClass).GetCustomAttributes(false)
+
      Console.WriteLine("  {0}", attr.GetType().Name)
+
    Next
+

          
+
    ' 派生クラスのすべての属性を取得 (継承した属性も含める)
+
    Console.WriteLine("DerivedClass (inherited = true)")
+

          
+
    For Each attr As Object In GetType(DerivedClass).GetCustomAttributes(true)
+
      Console.WriteLine("  {0}", attr.GetType().Name)
+
    Next
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
BaseClass
+
  NotInheritedAttribute
+
  InheritedAttribute
+
DerivedClass (inherited = false)
+
  CustomAttribute
+
DerivedClass (inherited = true)
+
  CustomAttribute
+
  InheritedAttribute
 
}}
}}
 

        

        
~
BaseClassでは適用した二つの属性が取得できるのに対し、DerivedClassではGetCustomAttributes()メソッドの引数inheritedによって結果が異なります。 引数inheritedがfalseの場合は、その型で適用されている属性のみが取得されるのに対し、trueの場合はその型で適用されている属性と継承された属性の両方が取得されます。
この例を見てわかるとおり、クラス自体にはCopyright属性の他にもDebuggerStepThrough属性を指定していますが、 CopyrightAttributeクラスの属性だけを取得しているので一覧には表示されません。 また、型情報からメソッドの情報を取得すると、その基底クラスのメソッドも含めた全てのメソッドが取得されます。 これらの型情報、メソッド情報からそれぞれGetCustomAttributes()メソッドを呼び出すことで、その対象に指定されている属性を取得することができます。
+

          
+
***InheritedとAllowMultiple
+
AllowMultipleがfalseの場合、基底クラスでInheritedがtrueの属性を適用し、かつ派生クラスでも同じ属性を適用すると、基底クラスの属性が''上書き''されます。 AllowMultipleがtrueの場合は上書きされず、複数個指定した場合と同じように扱われます。
+

          
+
#tabpage(C#)
+
#code(cs){{
+
using System;
+

          
+
// 継承される属性
+
[AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
+
public class InheritedAttribute : Attribute
+
{
+
  public string Value;
+

          
+
  public override string ToString()
+
  {
+
    return Value;
+
  }
+
}
+

          
+
// 継承されない属性
+
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
+
public class NotInheritedAttribute : Attribute
+
{
+
  public string Value;
 

        

        
~
  public override string ToString()
また、この例ではインスタンスから型情報を取得する方法で属性を取得していますが、インスタンスではなく直接型から型情報を取り出すという方法もとれます。 どちらを用いるかは属性の性質により定まると思います。
+
  {
+
    return Value;
+
  }
+
}
+

          
+
// 基底クラス
+
[Inherited(Value = "Base-Inherited"), NotInherited(Value = "Base-NotInherited")]
+
class BaseClass
+
{
+
}
+

          
+
// 派生クラス
+
[Inherited(Value = "Derived-Inherited"), NotInherited(Value = "Derived-NotInherited")]
+
class DerivedClass : BaseClass
+
{
+
}
+

          
+
class Sample {
+
  public static void Main()
+
  {
+
    // 基底クラスのすべての属性を取得
+
    Console.WriteLine("BaseClass");
+

          
+
    foreach (object attr in typeof(BaseClass).GetCustomAttributes(false)) {
+
      Console.WriteLine("  {0}", attr.ToString());
+
    }
+

          
+
    // 派生クラスのすべての属性を取得 (継承した属性は含めない)
+
    Console.WriteLine("DerivedClass (inherited = false)");
+

          
+
    foreach (object attr in typeof(DerivedClass).GetCustomAttributes(false)) {
+
      Console.WriteLine("  {0}", attr.ToString());
+
    }
+

          
+
    // 派生クラスのすべての属性を取得 (継承した属性も含める)
+
    Console.WriteLine("DerivedClass (inherited = true)");
+

          
+
    foreach (object attr in typeof(DerivedClass).GetCustomAttributes(true)) {
+
      Console.WriteLine("  {0}", attr.ToString());
+
    }
+
  }
+
}
+
}}
+
#tabpage(VB)
+
#code(vb){{
+
Imports System
+

          
+
' 継承される属性
+
<AttributeUsage(AttributeTargets.Class, Inherited := True, AllowMultiple := True)> _
+
Public Class InheritedAttribute
+
  Inherits Attribute
+

          
+
  Public Value As String
+

          
+
  Public Overrides Function ToString() As String
+
    Return Value
+
  End Function
+
End Class
+

          
+
' 継承されない属性
+
<AttributeUsage(AttributeTargets.Class, Inherited := False, AllowMultiple := True)> _
+
Public Class NotInheritedAttribute
+
  Inherits Attribute
+

          
+
  Public Value As String
+

          
+
  Public Overrides Function ToString() As String
+
    Return Value
+
  End Function
+
End Class
+

          
+
' 基底クラス
+
<Inherited(Value := "Base-Inherited"), NotInherited(Value := "Base-NotInherited")> _
+
Class BaseClass
+
End Class
+

          
+
' 派生クラス
+
<Inherited(Value := "Derived-Inherited"), NotInherited(Value := "Derived-NotInherited")> _
+
Class DerivedClass
+
  Inherits BaseClass
+
End Class
+

          
+
Class Sample
+
  Public Shared Sub Main()
+
    ' 基底クラスのすべての属性を取得
+
    Console.WriteLine("BaseClass")
+

          
+
    For Each attr As Object In GetType(BaseClass).GetCustomAttributes(false)
+
      Console.WriteLine("  {0}", attr.ToString())
+
    Next
+

          
+
    ' 派生クラスのすべての属性を取得 (継承した属性は含めない)
+
    Console.WriteLine("DerivedClass (inherited = false)")
+

          
+
    For Each attr As Object In GetType(DerivedClass).GetCustomAttributes(false)
+
      Console.WriteLine("  {0}", attr.ToString())
+
    Next
+

          
+
    ' 派生クラスのすべての属性を取得 (継承した属性も含める)
+
    Console.WriteLine("DerivedClass (inherited = true)")
+

          
+
    For Each attr As Object In GetType(DerivedClass).GetCustomAttributes(true)
+
      Console.WriteLine("  {0}", attr.ToString())
+
    Next
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
BaseClass
+
  Base-NotInherited
+
  Base-Inherited
+
DerivedClass (inherited = false)
+
  Derived-NotInherited
+
  Derived-Inherited
+
DerivedClass (inherited = true)
+
  Derived-NotInherited
+
  Derived-Inherited
+
  Base-Inherited
+
}}