Visual Basic テクニック |
存在は知っていてもなかなか活用されていないという「属性」を正面から取り上げ、すぐに使用できるいくつかの属性を具体的な使用例とともに紹介します。属性活用の第一歩として本記事を参考にしてください。
概要 ・属性の基本事項の確認 ・Obsolete属性を使用すると、該当メンバが使用された場合警告を出したり、エラーにすることができる。 ・DebuggerStepThrough属性を使用すると、ステップイン実行を無効にすることができる。 ・DebuggerDisplay属性を利用すると、デバッグ用のウィンドウに表示される情報をカスタマイズすることができる。 ・リフレクションを使用することで、自分で属性の利用方法をカスタマイズすることができる。 |
属性とはクラスやメソッドなどの追加情報のことで、自作のクラスめメソッドにも手軽に属性を追加することができます。
属性は特定のシーンでは実に有効にはたらくのですが、案外と属性を利用しているプログラマは少ないようで、埋もれた機能となりつつあります。これは少しもったいない気がします。
属性が利用されない原因の1つは、属性がどこでどのように役に立つのかよくわからなかったり情報が不足していたりするからでしょう。確かに属性というのはプログラムをする上で避けて通れないわけではないので、書籍やWebサイトなどの解説記事でもなかなか正面から解説されないようです。
そこで、今回はいくつかの属性を実際の使用例とともに紹介します。属性は無数にあるのでここで紹介する例だけではさびしい限りですが、これらの例をあしがかりに属性活用の道を踏みだしてもらえれば幸いです。
具体的な紹介に入る前に属性の基本事項を簡単に確認しておきます。属性を使用する上で基本となる事項をまとめてみました。この他、属性についての基本的な説明は初級講座第32回 属性 もご覧ください。
例:メソッドにObsolete属性を適用する例
<Obsolete()>Private Function GetUserName() As String
'メソッドの内容は省略
End Function■リスト1
<Obsolete()> _
Private Function GetUserName() As String
'メソッドの内容は省略
End Function■リスト2
例:メソッドにObsolete属性とDebuggerStepThrough属性を適用する例
<Obsolete()> _
<DebuggerStepThrough()> _
Private Function GetUserName() As String
'メソッドの内容は省略
End Function■リスト3
<Obsolete(), DebuggerStepThrough()> _
Private Function GetUserName() As String
'メソッドの内容は省略
End Function■リスト4
Obsolete属性(読み方:Obsolete = オブサリート)は、対象が旧式であることを示します。Obsolete属性がついているメソッドやクラスを使用した場合、コンパイラはそれが旧形式であると言う警告を出力し、これはVisual Studioのエラー一覧にも表示されます。
これが便利なのは、ある程度規模が大きいプログラムを作成しているときに、たとえば今まで使用していたクラスAは問題があるので、今後は使用しないようにして順次クラスBに切り替えていきたいという場合です。このときクラスAにObsolete属性をつければエラー一覧ですぐにクラスAを使用している個所の一覧が特定できますし、今後誰かが間違ってクラスAを使用しようとした場合でも、すぐに警告を示す緑色の波線が表示されるので気がつくことができます。
さらに、Obsolete属性の引数を利用することで、警告として表示するメッセージのカスタマイズしたり、警告ではなくエラーとして扱うという制御を行うことも可能になります。
警告として表示するメッセージをカスタマイズするには、以下の例のように単純にObosolete属性の引数にメッセージを指定します。
<Obsolete("今後はMy.User.Nameを使用してください。")>
_ Private Function GetUserName() As String 'メソッドの内容は省略 Return "" End Function |
■リスト5
この場合に、他の個所からGetUserNameメソッドを呼び出すと、警告メッセージには以下の通り「'Private Function GetUserName() As String'は旧形式です:'今後はMy.User.Nameを使用してください。'」と表示されます。
■画像1
Obosolete属性を警告ではなく、エラーとして扱うには第2引数にTrueを指定します。
<Obsolete("今後はMy.User.Nameを使用してください。",
True)> _ Private Function GetUserName() As String 'メソッドの内容は省略 Return "" End Function |
■リスト6
警告の場合は、メッセージは表示されるもののビルドして実行することが可能ですが、エラーの場合はビルドすることができなくなります。これはObsoleteでマークされた要素を使用しないことを強制するときに便利です。
もちろん、使用してはいけないメソッドやクラスなどをプログラムに記述する意味はありませんから、エラー扱いのメソッドやクラスは近いうちにプロジェクトから完全に削除される場合などに過渡的に使用することが想定されています。
私は結構このObsolete属性を便利に使用しています。プロジェクトが進んでいくとどうしても当初の想定とは異なる実装が必要になってくる部分がでてきます。このとき開発チームのメンバに口頭や書面で「今後は○○は使用しないで、△△を使用してください。」と伝えると同時にObsolete属性で親切な説明をつけておくと効果抜群です。
DebuggerStepThrough属性はデバッグ時にステップ実行の対象にならないことを示します。
通常はデバッグ時のステップ実行は非常に重要な機能で、デバッグにはなくてはならないものなのですが場合によっては邪魔になります。このような場合にDebuggerStepThrough属性を使用すると、ステップ実行させたい部分だけステップ実行可能になり、させたくない部分はステップ実行ができないように仕組みことが可能です。
これだけの説明だと、ステップインとステップオーバーを使い分けるだけで済むように思えるかもしれませんが、実際のシステム開発ではこれだけではすまないことがよくあります。
たとえば、次のプログラムをステップ実行するときのことを考えてみましょう。
Dim ID1
As String =
TextBox1.Text Dim ID2 As String = TextBox2.Text Dim Result As String Result = GetData(GetValueA(ID1), GetValueB(ID2)) If Len(Result) = 0 Then Return End If |
■リスト7
ここで、GetData、GetValueA、GetValueBはすべて自作のメソッドです。
このとき、Result = GetData(GetValueA(ID1), GetValueB(ID2))の行で、GetDataメソッドの中にステップインしてデバッグしたい場合、実際にF11やF8などを押してステップインしようとすると、GetValueAの中をステップ実行してしまいます。そのまま実行を続けてGetValueAを抜けても今度はGetValueBをステップ実行し、それが終わってからようやくGetDataの中にステップインすることができます。
同じ行の中で3つのメソッドを呼び出しているので、その中の特定の1つにだけステップインしてほかは無視すると言うことができないのです。
このようなときでもメソッドをDebuggerStepThrough属性でマークしておけば、そのメソッドにはステップインしないで済みます。
たとえば、次のようにします。
<DebuggerStepThrough()> _ Private Function GetValueA(ByVal Value As Object) As String 'メソッドの内容は省略 Return "" End Function |
■リスト8
DebuggerStepThrough属性はエディットコンティニュー機能を使用してデバッグ時に付け加えても動作しません。デバッグ開始前にあらかじめ指定しておく必要があります。
そのためシステムの中でも基盤となるライブラリやフレームワークの部分など事前に十分にテストされていて、 他の個所にデバッグレベルで影響を与えないような部分などを対象に使用します。
たとえば、VBが自動生成するInitializeComponentメソッドにはDebuggerStepThrough属性がついています。このことは普段は非表示になっているForm1.Designer.vbファイルの中を見ると確認できます。
DebuggerStepThrough属性にはステップ実行を無効にするだけではなく、ブレークポイントを無効にしたり、対象のメソッドを呼び出し履歴から隠す機能があります。
DebuggerStepThrough属性の効果 |
ステップインを無効にする |
ブレークポイントを無効にする |
Stopステートメントで呼び出し元で停止するようにする |
対象のメソッド・プロパティを呼び出し履歴から隠す |
DebuggerStepThrough属性がついたメソッドから通常のメソッドを呼び出しているときにステップインを行うと、デバッガはDebuggerStepThrough属性のついたメソッドをジャンプしていきなり次に呼び出されているメソッドで止まります。デバッグしているプログラマから見ると呼び出していないメソッドにジャンプしているかのように見えるわけです。
このとき呼び出し履歴を確認すると、DebuggerStepThrough属性がついたメソッドは履歴には表示されず該当個所には[外部コード]とだけ表示されます。
DebuggerStepThroughと似たような属性にDebuggerNonUserCode属性とDebuggerHidden属性があります。通常の機能はまったく同じだと考えて間違いありません。DebuggerHidden属性の場合は、呼び出し履歴に[外部コード]すら表示されず完全に履歴が隠蔽されますが違いはそれだけです。
DebuggerNonUserCodeはVB2005以降で使用可能です。
これらの3つの属性は「'マイコードのみ'設定を有効にする」をオフにした場合には動作が変わります。この設定は[ツール]メニューの[オプション]画面で[デバッグ] - [全般]ページにあります。既定値はオンになっており、Express Editionではこの設定を変更することはできません。
この設定のオン・オフと3つの各属性の動作の違いを下の表にまとめておきます。
属性 | 'マイコードのみ' オン (既定の設定) | 'マイコードのみ' オフ | ||||
ステップイン | ブレークポイント | 呼び出し履歴 | ステップイン | ブレークポイント | 呼び出し履歴 | |
DebuggerNonUserCode | 無効 | 無効 | [外部コード] | 有効 | 有効 | 有効 |
DebuggerStepThrough | 無効 | 無効 | [外部コード] | 無効 | 有効 | 有効 |
DebuggerHidden | 無効 | 無効 | 表示なし | 無効 | 無効 | 表示なし |
■表1
なお、どの属性でもいえることですが、属性自体は明示的に特定の機能を対象としているわけではないので、MSDNライブラリにはこのような情報はあまり書かれていません。ですので、ここには私が気が付いている部分を掲載しただけで、Visual Studioの他の部分で上記3属性によって反応や動作が変わる部分があるかもしれません。みつけたら教えてください。
マイコードとは文字通り自分が作業中のコードを指しており、ライブラリなどの部分と区別するときに使用します。マイコードとそうでない部分を区別する目的は本文中で扱っているようにデバッグの範囲を明確化するためです。 コードがマイコードに該当するかしないかの判断はpdbファイルや最適化の有無によって行われます。マイコードのついての情報は次のWebサイトもご覧になってください。 http://msdn2.microsoft.com/ja-jp/library/h5e30exc(VS.80).aspx |
VB.NET (2002)とVB.NET2003では「マイコードのみ」を設定する場所が見つけられませんでした。ひょっとしたら、「マイコード」という考え方自体がないのでしょうか? |
DebuggerDisplay属性はVB2005から導入された属性で、デバッグ用のウィンドウに表示される情報の表示形式をカスタマイズする属性です。デバッグ用の各種ウィンドウでの変数の表示の仕方などを少しカスタマイズしたい場合などに利用します。
たとえば、次のプログラムを考えてみます。
Public
Class Form1 Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Dim Products As New List(Of ProcustInfo) Products.Add(New ProcustInfo("001", "ナットA-001", 1200)) Products.Add(New ProcustInfo("001", "ワッシャーA-001", 860)) Products.Add(New ProcustInfo("001", "ネジA-001", 2400)) Stop End Sub End Class |
Public
Class ProcustInfo Public ID As String Public Name As String Public Stock As Integer Public Sub New(ByVal ID As String, ByVal Name As String, ByVal Stock As Integer) Me.ID = ID Me.Name = Name Me.Stock = Stock End Sub End Class |
■リスト9
このプログラムではButton1をクリックすると商品の一覧を変数Productsに追加していきます。Stopのところで実行が停止するのでProductsに追加された商品の一覧をデバッグ用のウィンドウで確認してみましょう。ウォッチウィンドウでもローカルウィンドウでも同じ結果になりますが、ここではクイックウォッチで見たときの表示を掲載します。
■画像2
デバッグ用のウィンドウではこのように3つの商品が追加されていることはすぐにわかりますが、個々の商品の内容がどのようになっているかはさらに詳細を展開しないとわかりません。
■画像3
しかし、一つずつ詳細を展開していくのは効率が悪い場合があります。たとえば、この中の1つでデータがおかしいものがあって、それがどれだか突き止めたい場合、商品の数にもよりますが1つずつ展開して確認していくのでは時間がかかります。
このような時にDebuggerDisplay属性を使用すると、表示をカスタマイズして疑似的な一覧表のように表示することができます。
結果からみていただくと表示方法を次のようにカスタマイズすることが可能です。
■画像4
あきらかに大分見やすくなり、この型のコレクションを利用したときのデバッグ効率は非常な高上が期待できそうです。
このようにカスタマイズするには上記のコードでProductInfoクラスに次のようにDebuggerDisplay属性を適用します。
以下には該当部分のみ抜粋します。
<DebuggerDisplay("{ID}
{Name} 在庫数:{Stock}")> _ Public Class ProcustInfo |
■リスト10
このコードと上記の画像を見比べていただければ説明は必要ないと思いますが、{ }でくくった部分が実際の変数やプロパティに置き換わると言う仕様です。これには内部的にリフレクションの機能が使用されていると推測されます。
この部分には変数やプロパティだけでなくメソッドも指定できるので、本格的に活用して大幅なカスタマイズを加えることもできます。
<DebuggerDisplay("{DisplayDebugView}")>
_ Public Class ProcustInfo Public ID As String Public Name As String Public Stock As Integer Public Sub New(ByVal ID As String, ByVal Name As String, ByVal Stock As Integer) Me.ID = ID Me.Name = Name Me.Stock = Stock End Sub |
Public
Function DisplayDebugView()
As
String Return Me.Name & "の在庫数は" & Stock End Function End Class |
■リスト11
また、既定でいくつかの構文めいたものが用意されているようですがMSDNライブラリに詳しい記載がないため詳細は不明です。
とは言え、これだけの知識で十分役立てることができると思います。
属性の設定内容はリフレクションを使って取得することができるので、各種属性とリフレクション機能を組み合わせることによってアイディア次第でいろいろなことを行うことができます。これはマイクロソフトが提供する様々な機能の中でも用いられている手段であって非常に応用範囲の広い手法と言えます。
まず、簡単にリフレクションを使ってあるメソッドに適用されているDescription属性の値を読み込む例を紹介します。
Private Sub Button1_Click(ByVal
sender As System.Object,
ByVal e As
System.EventArgs) Handles Button1.Click Dim Method As Reflection.MethodBase Method = Me.GetType.GetMethod("TestA", Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Instance) Dim Attr As System.ComponentModel.DescriptionAttribute Attr = Method.GetCustomAttributes(GetType(System.ComponentModel.DescriptionAttribute), False)(0) MsgBox(Attr.Description) End Sub |
<System.ComponentModel.Description("これはテスト用のメソッドです。")>
_ Private Sub TestA() 'メソッドの内容は省略 End Sub |
■リスト12
この例を実行すると「これはテスト用のメソッドです。」と表示されます。
今回はこの例の詳細な説明は省略します。詳細を知りたい場合はMSDNライブラリなどでSystem.Reflection名前空間について調べてください。
このような機能を利用すれば、たとえばクラスの設計図を自動的に出力するような仕組みを実装したり、プログラム内部でも属性によって処理を切り換えると言うことが可能になります。もっとも属性とは本来何の機能もないものとして用意されている仕組みですからアプリケーションの挙動自体を属性によって切り替えることは避けた方がよいでしょう。
今回はDescription属性をサンプルで使用しましたが、属性自体を自分で作成することもできます。自作の属性を有効に活用するには上記のようなリフレクションによる手法が必須になります。そうでなければ属性に応じて何か追加の動作を行うと言うことは一切できません。
今回はすぐに使える属性を抜粋して紹介しましたが、他にもいろいろと便利な属性がありますし、特定のシーンに限定すれば積極的に活用すべき属性も多々あります。たとえば、カスタムコントロールを作成する場合はCategory属性やTypeConverter属性などプロパティウィンドウの動作を指定できるようないろいろな属性を利用することになるでしょう。
とは言え、どのような属性があってそれがどのような影響を与えるものなのかと言う情報は存外に少なく、「属性を活用しよう!」と掛け声だけ勇ましくしてもなかなか情報量が付いてこないのが現状です。
ですので、Webや雑誌のサンプルで見慣れない属性をみつけたら簡単にでも調べて自分の物にしていったり、MSDNライブラリをあさってみるなどすることをお勧めします。まずはざっとでもどんなものがあるのかつかんでおくとよいでしょう。
しかしながら、属性の多くはMSDNライブラリの説明を見ても何のためにどこで使えばよいのかわかりにくいです。私もまだまだたくさんの属性を見逃しているはずです。みなさんも便利な属性の使い方を発見したらぜひ教えてください。