連載

改訂版
プロフェッショナルVB.NETプログラミング

Chapter 14 属性

株式会社ピーデー 川俣 晶
2004/09/02
Page1 Page2 Page3 Page4

 本記事は、(株)技術評論社が発行する書籍『VB6プログラマーのための入門 Visual Basic .NET 独習講座』の一部分を許可を得て転載したものです。同書籍に関する詳しい情報については、本記事の最後に掲載しています。
 
属性を自作する

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

 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
リスト14-16 メソッドにプログラマーの名前を付加するためのAuthor属性を記述したプログラム

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

1: method Test1 is written by Ichiro
2: method Test2 is written by Jiro
3: method DumpAuthor is written by Saburo
リスト14-17 リスト14-16の実行結果

 さて、肝心の中身だが、今回のソースについては説明することが多いので、心して読んでいただきたい。

 まず、属性本体の定義を見てみよう。属性は一種のクラスであると説明したとおり、クラスとして定義する。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メソッドが不可欠である。それと同じように、自作属性を活用するには、それを活用するためにコードを記述する必要がある。リスト14-16では、20〜27行目のDumpAuthorメソッドがそれに当たる。

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

 まず、リスト14-16の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名を取り出して出力している。

クラスに付く属性

 前節のサンプル・プログラム(リスト14-16)はメソッドに付く属性であったが、属性はメソッド以外のものに付けることもできる。メソッドに付く属性の例を、クラスを対象としたものに書き換えてみよう。

 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
リスト14-18 クラスに対して指定するAuthor属性を記述したプログラム(このコードが含まれるプロジェクトの名前は「Sample006n」)

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

1: class Test1 is written by Ichiro
2: class Test2 is written by Jiro
3: class Form1 is written by Saburo
リスト14-19 リスト14-18の実行結果

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

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

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

複数の属性を持たせる

 クラスを書いたプログラマーの名前を属性で記述可能にした場合、実際に使うとすぐに問題が出るだろう。チーム開発なら、1つのクラスを1人のプログラマーが最初から最後まで開発して、ほかのプログラマーが関与しないということは考えにくい。では、書いたプログラマーが2人いた場合は、どう対処すればよいのだろうか? これは、Author属性を1つのクラスに2つ以上記述可能にすればよいリスト14-20は、実際にそれを実現したサンプル・プログラムである。

 1: <AttributeUsage(AttributeTargets.Class, AllowMultiple:=True)> 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"), Author("Saburo")> 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("Sample007n." + 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
リスト14-20 クラスに対して複数指定可能にしたAuthor属性を記述したプログラム

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

1: class Test1 is written by Ichiro
2: class Test2 is written by Saburo
3: class Test2 is written by Jiro
4: class Form1 is written by Saburo
リスト14-21 リスト14-20の実行結果

 このソースでまず注目してもらいたいのは、17行目である。“<Author("Jiro"), Author("Saburo")>”というように、Author属性が2つ記述されている。このように、同じ属性を2回以上記述することは不可能ではない。だが、何もせずそのまま記述しても、ビルド時にエラーになるだけである。同じ属性を同じターゲットに2回以上記述するには、属性を宣言する際に、1行目のように“AllowMultiple:=True”という指定を入れなければならない。ただし、ここで代入と同じように“AllowMultiple=True”と書いてはいけないことに注意が必要である。イコール(=)記号の前に、コロン(:)記号が必要である。これは、名前付き引数と呼ばれるものである(名前付き引数を参照)。

 この修正の結果、27行目で取得されるAuthor属性クラスの配列は、"Test2"が指定されたとき、1個ではなく2個の要素を含むようになる。つまり、指定された2個の属性が、要素数2の配列を通して取得できたということである。


 INDEX
  [連載] 改訂版 プロフェッショナルVB.NETプログラミング
  Chapter 14 属性
    1.属性とは何か?/固定長配列を指定するVBFixedArray属性/オブジェクトのシリアライズ
    2.属性の名前のバリエーション/Declare文の引数型を変換するMarshalAs属性/条件付きメソッド
  3.属性を自作する/クラスに付く属性/複数の属性を持たせる
    4.名前付き引数/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メールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)
- PR -

注目のテーマ

業務アプリInsider 記事ランキング

本日 月間
ソリューションFLASH