連載

プロフェッショナルVB.NETプログラミング
―― VB 6プログラマーのためのVB.NET入門 ――

第29回 属性(後編)

(株)ピーデー
川俣 晶
2002/12/14

Page1 Page2 Page3

属性を自作する

 前回では、.NET Frameworkが提供する属性ばかりを紹介したが、属性はシステムがあらかじめ用意したものに限らない。一般のプログラマーが自分で新しい属性を定義して使用することもできる。例として、メソッドを記述したプログラマーの名前をソース・コード中に挿入するための属性を作成してみよう。

  1:   <AttributeUsage(AttributeTargets.Method)> Public Class AuthorAttribute
  2:     Inherits Attribute
  3:     Private _name As String
  4:     Public Sub New(ByVal name As String)
  5:       _name = name
  6:     End Sub
  7:     Public ReadOnly Property name() As String
  8:       Get
  9:         Return _name
 10:       End Get
 11:     End Property
 12:   End Class
 13:
 14:   <Author("Ichiro")> Public Sub Test1()
 15:   End Sub
 16:
 17:   <Author("Jiro")> Public Sub Test2()
 18:   End Sub
 19:
 20:   <Author("Saburo")> Public Sub DumpAuthor(ByVal methodName As String)
 21:     Dim info As System.Reflection.MethodInfo = Me.GetType().GetMethod(methodName)
 22:     Dim list() As Object = info.GetCustomAttributes(GetType(AuthorAttribute), False)
 23:     Dim item As AuthorAttribute
 24:     For Each item In list
 25:       Trace.WriteLine("method " & methodName & " is written by " & item.name)
 26:     Next
 27:   End Sub
 28:
 29:   Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
 30:     DumpAuthor("Test1")
 31:     DumpAuthor("Test2")
 32:     DumpAuthor("DumpAuthor")
 33:   End Sub
メソッドにプログラマーの名前を付加するためのAuthor属性を記述したサンプル・プログラム1

 これを実行すると以下のようになる。

 1: method Test1 is written by Ichiro
 2: method Test2 is written by Jiro
 3: method DumpAuthor is written by Saburo
サンプル・プログラム1の実行結果

 さて、肝心の中身だが、これには説明することが多くあるので、心して読んでいただきたい。

 まず、属性本体の定義を見てみよう。属性は一種のクラスであると説明したとおり、クラスとして定義する。1〜12行目で定義されているクラスが、自作属性ということになる。しかし、ただ単にクラスを記述するだけでは属性にならない。3つの追加作業を行わねばならない。1つ目は、2行目に記述したようにAttributeクラス(System.Attributeクラス)を継承することである。2つ目は、クラスの名前をAttributeという文字列で終わるようにすることである。この文字列は、すでに述べたとおり、属性として記述する際には省略可能である。3つ目は、1行目に見られるようなAttributeUsage属性を付加して、それが何に付く属性であるかを示すことである。ここでは、AttributeTargets.Methodが指定されているが、これはメソッドに付く属性であることを意味する。AttributeTargetsは列挙型で、ほかにもクラスなどのバリエーションが定義されている。必要なものを選んで指定すればよい。Or演算子で区切って、

[AttributeUsage(AttributeTargets.Method Or AttributeTargets.Class)]

というように複数を並べて指定することもできる。こうするとメソッドにもクラスにも付けられる属性になる。

 これで新しい属性を誕生させることができた。これ以後、例えば14行目の<Author("Ichiro")>のように、メソッドの宣言の前に、この属性を記述することが可能となる。Authorという名前は、もちろん、AuthorAttributeというクラス名から、Attributeという文字列を省略した名前である。属性には、1つの文字列が引数として指定されているが、これは4〜6行目のコンストラクタの引数がそのまま使われた結果である。つまり、4行目に記述された引数ByVal name As Stringに対応するものが、"Ichiro"という文字列である。

 さて、ここでもう1つ考えねばならないことがある。属性を自作し、それをメソッドなどに付け加えることは簡単だが、それだけでは何の効果も発揮しないということである。例えば、VBFixedStringAttribute属性が効果を発揮するためには、VBFixedStringAttribute属性の情報を活用するFilePutメソッドやFileGetメソッドが不可欠である。それと同じように、自作属性を活用するには、それを活用するためにコードを記述する必要がある。このサンプル・プログラムでは、20〜27行目のDumpAuthorメソッドがそれに当たる。

 DumpAuthorメソッドの中では、「リフレクション」という機能が使用されている。これは、プログラムそのものに対する情報にアクセスする機能である。例えば、あるクラスにどんな名前のメソッドが存在するかを実行中に調べることができる。クラスやメソッドの情報を調べる際に、それに付随する属性の情報も調べることができる。リフレクションはクラス・ライブラリの興味深い機能の1つであって、必ずしもVB.NET専用の機能というわけではないので、この連載では取り上げない。しかし、このサンプル・プログラムを読むために必要な知識をここで解説しておこう。

 まず21行目を見ていただきたい。System.Reflection.MethodInfoクラスは、メソッドに関する情報を保持するクラスである。その中には、メソッドの名前や、属性などの情報が含まれる。この情報を取り出すために、Me.GetType().GetMethod(methodName)という式を計算している。Meはこの場合、現在処理中のフォームのインスタンスを示している。これにGetTypeメソッドを呼び出すことで、そのデータ型(Form1クラス)についての情報を持つSystem.Typeクラスのインスタンスを取得できる。さらに、そのインスタンスのGetMethodメソッドを呼び出すことで、引数で指定した名前のPublicなメソッドに関する情報が取得できるわけである。

 次に22行目を見ていただきたい。21行目で取得したメソッド情報に対して、GetCustomAttributesメソッドを呼び出している。これが自作属性の情報を得るために使用できるメソッドである。第1引数に取得したい属性を表すクラスに対応するSystem.Type型の値を渡している。ある型に対するSystem.Type型の値は、GetType演算子で得ることができる。第2引数は、属性クラスが継承されたクラスかどうかを指定するものだが、ここでは継承関係が絡み合うような使い方はしていないので、シンプルにfalse(継承されていない)を指定している。

 GetCustomAttributesメソッドの戻り値はObject型の配列だが、内容はもちろん、AuthorAttribute型の配列である。とはいえ、AuthorAttribute属性は1つのメソッドに複数付けることを許可する指定を入れていないので、配列といっても、通常は要素数1の配列になることが予想される。AuthorAttribute型の配列が得られれば、後は通常のプログラミングで対応できる範囲である。AuthorAttributeクラスのnameプロパティを経由してAuthor名を取り出して出力している。

クラスに付く属性

 上の例はメソッドに付く属性であったが、クラスに付く属性も記述してみよう。

  1: <AttributeUsage(AttributeTargets.Class)> Public Class AuthorAttribute
  2:   Inherits Attribute
  3:   Private _name As String
  4:   Public Sub New(ByVal name As String)
  5:     _name = name
  6:   End Sub
  7:   Public ReadOnly Property name() As String
  8:     Get
  9:       Return _name
 10:     End Get
 11:   End Property
 12: End Class
 13:
 14: <Author("Ichiro")> Public Class Test1
 15: End Class
 16:
 17: <Author("Jiro")> Public Class Test2
 18: End Class
 19:
 20: <Author("Saburo")> Public Class Form1
 21:   Inherits System.Windows.Forms.Form
 22:
 23: …Windows フォーム デザイナで生成されたコード…
 24:
 25:   Public Sub dumpAuthor(ByVal className As String)
 26:     Dim targetType As Type = Type.GetType("Sample006n." + className)
 27:     Dim list() As Object = targetType.GetCustomAttributes(GetType(AuthorAttribute), False)
 28:     Dim item As AuthorAttribute
 29:     For Each item In list
 30:       Trace.WriteLine("class " & className & " is written by " & item.name)
 31:     Next
 32:   End Sub
 33:
 34:   Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
 35:     dumpAuthor("Test1")
 36:     dumpAuthor("Test2")
 37:     dumpAuthor("Form1")
 38:   End Sub
 39: End Class
クラスに対して指定するAuthor属性を記述したサンプル・プログラム2

(このコードが含まれるプロジェクトの名前は「Sample006n」)

 これを実行すると以下のようになる。

 1: class Test1 is written by Ichiro
 2: class Test2 is written by Jiro
 3: class Form1 is written by Saburo
サンプル・プログラム2の実行結果

 まず、14行目や17行目で、Test1やTest2などのクラスにAuthor属性が付いていることが分かると思う。クラスに付加する属性を宣言するには、1行目にあるように、AttributeUsage属性に、AttributeTargets.Methodではなく、AttributeTargets.Classという値を指定する必要がある。

 クラスに付く属性に関する情報を得るために、ここではType(System.Type)クラスのGetTypeメソッドを使っている。これは指定された名前のクラスに関するTypeクラスのインスタンスを返すSharedなメソッドである。このメソッドの引数には、クラスのフルネームを指定する必要があるので、プロジェクト名の文字列("Sample006n.")を連結してフルネームとしている。もし、違うプロジェクト名でプロジェクトを作成した場合、この文字列も修正しなければ正常に動作しない。

 クラスに関する情報を持つTypeクラスのインスタンスが得られたら、それに対してGetCustomAttributesメソッドを呼び出せば、カスタム属性の情報を取り出すことができる。このあたりは、メソッドの場合(サンプル・プログラム1)と同じである。


 INDEX
  連載 プロフェッショナルVB.NETプログラミング
  第29回 属性(後編)
  1.属性を自作する
    2.複数の属性を持たせる
    3.Webサービスで使われる属性の概要
 
「プロフェッショナルVB.NETプログラミング」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間