Visual Basic 初級講座 [改訂版]
VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

第24回 属性

2020/11/1

この記事が対象とする製品・バージョン

VB2019 Visual Basic 2019 対象です。
VB2017 Visual Basic 2017 対象です。
VB2015 Visual Basic 2015 対象です。
VB2013 Visual Basic 2013 対象です。
VB2012 Visual Basic 2012 対象です。
VB2010 Visual Basic 2010 対象です。
VB2008 Visual Basic 2008 対象です。
VB2005 Visual Basic 2005 対象です。
VB.NET 2003 Visual Basic.NET 2003 対象外ですがほとんどの説明があてはまるので参考になります。
VB.NET 2002 Visual Basic.NET (2002) 対象外ですがほとんどの説明があてはまるので参考になります。
VB6対応 Visual Basic 6.0 × 対象外です。

 

目次

1.属性

属性(ぞくせい)とは、変数やメソッドやクラスなどにつける目印や追加情報のことです。属性にはたくさんの種類があらかじめ用意されており、これを使うことでメソッドの使い方や、分類、そのメソッドは旧式であるから代わりに○○メソッドを使うべきだなどいろいろな情報を追加することができます。

属性自体には何の機能ありません。ツールやプログラムの中には特定の属性が付いている場合に動作が変わるものがあり、そのようなツールやプログラムとうまく連携するために属性を使うことがあります。そのようなツールの例としては、Visual Studioのプロパティウィンドウや、オブジェクトブラウザー、デバッガーなどがあります。一方、目印や追加情報以上の意味はまったくなく、プログラムやツールの機能にまったく影響しないコメントのように属性を使う場合もあります。

※オブジェクトブラウザーは初級講座では登場していません。デバッガーはデバッグ時に変数の内容を見たりする機能を提供しているものです。

追加できる情報の種類ごとに属性が定義されています。あらかじめ.NETに用意されている属性もたくさんありますし、自分で作成することもできます。

次の例は、メソッドにカテゴリー(分類)の情報を追加します。

VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

<System.ComponentModel.Category("健康")>
Private Function BMI(height As Double, weight As Double) As Double

    Return weight / (height * height)

End Function

 

メモ メモ  - VB2008以前の場合は、属性の行の最後に _ が必要です。

VB2008以前の場合は、次の例のように属性の行の最後に  _ が必要です。 _ を1文字追加すればよいのではなく、半角スペースと _ を両方が必要です。

VB2002 VB2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

<System.ComponentModel.Category("健康")> _
Private Function BMI(height As Double, weight As Double) As Double

    Return weight / (height * height)

End Function

 _ はプログラム中で改行をするときに使う記号ですが、VB2010 からはこの記号なしで改行できるようになったので省略できるようになりました。

 

この例にあるとおり、属性を付加するに場合 < > を使用します。

属性と対象(この場合はBMIメソッド)は同じ行に記述することもできます。この方法だと1行が長くなってしまいますし、文頭の位置(この例の場合Private Functionの位置)が他の部分とそろわなくてでこぼこなプログラムになるので見にくくなりますから、私は好みではありません。

VB2002 VB2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

<System.ComponentModel.Category("健康")>Private Function BMI(height As Double, weight As Double) As Double

    Return weight / (height * height)

End Function

この記事では基本的に、記号なしの改行で属性の記述を記述します。そのため、VB2010以降でないとそのまま実行はできませんが、これは改行の仕様が違うだけであり、属性自体はVB.NET(2002)以降であれば同じように使用できます。

※逆に同じ行に属性があったほうが見やすいこともあることはあり、その場合は私は同じ行に属性を書くこともあります。このあたりは個人や開発チームの好みの方法を採用すればよいです。

 

属性はクラスの一種です。「カテゴリー(分類)」を表す属性は System.ComponentModel.CategoryAttributeクラス (読み方:ComponentModel=コンポーネントモデル、Category=カテゴリーアトリビュート)です。

属性を表すクラスは、名前の最後を Attribute にすることが慣例となっています。属性を適用するプログラムではこの「Attribute」を省略できるので、いちいち書くことありません。上記の例でも CategoryAttribute とは書かずに、Categoryと書いています。

口に出す場合も「Category属性」ということが通常です。「CategoryAttributeクラス」という表現をする人はまずいません。

追加できる情報は、カテゴリーの他にも多数あり、それぞれ属性が用意されています。

カテゴリー(分類)は理解しやすいので例にしましたが、実際のところ、メソッドにこの属性をつけてもプログラムの機能は何も変わりません。プロパティウィンドウは、この属性の情報を利用しており、プロパティウィンドウに表示される分類をこの属性から読み取ります。他の属性だと、何かが変わるものもありますし、何も変わらないものもあります。

属性を使って追加情報をつけることにどういった意味があり、どういう考え方なのか見ていきましょう。まずは、属性の構文から説明します。

 

2.属性の構文

2-1.属性の対象

属性を付加できる対象には次のようなものがあります。

属性ごとに何を対象にできるのかはあらかじめ決まっています。たとえば、変数用の属性をクラスに適用することはできません。ただし、クラスと変数の両方に適用できる属性というものも存在します。

次の例では変数に属性を適用します。

VB2010 VB2012 VB2013 VB2015 VB2017 VB2019


<System.ComponentModel.Description("変数valueの説明です。")>
Dim value As String

※前述したように、改行記号 _ を使うか、改行しないで記述すればVB2008以前でも同じように記述できます。この注記は今後省略します。

この例で使用している Description属性は説明情報を表す属性です。

メモ メモ  - XMLコメントとの違い

開発者がプログラムを行うときに役に立つような説明は XMLコメントで記述するほうが良いです。

VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

''' <summary>変数valueの説明です。</summary>
Dim value As String

Visual Studio のエディターはXMLコメントをインテリセンスで表示してくれるので便利です。属性がどのようなシーンで実際に役に立つのかは後で説明しますが、Description属性の場合、たとえば、プロパティウィンドウに表示されるメッセージとなります。

 

次の例ではメソッドに属性を適用します。

VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

<System.ComponentModel.Description("MyMethodメソッドの説明です。")>
Private Sub MyMethod()

    '中身は省略

End Sub

 

次の例ではクラスに属性を適用します。

VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

<System.ComponentModel.Description("TestClassクラスの説明です。")>
Public Class TestClass

    '中身は省略

End Class

 

この他にアセンブリに属性を付けることもできます。

アセンブリとは、大雑把に言うと、VBやC#のプログラムをビルドして生成されるexeファイルやdllファイルのことです。

※この説明は十分実用的ですが、正確ではありません。正確な説明はこちらを参照してください。小難しいです。

https://docs.microsoft.com/ja-jp/dotnet/standard/assembly/

 

アセンブリに対する属性はVisual Studioを使っている場合、ほとんど自動的に行われるので通常自分で記述することはありません。興味がある方は下の囲み記事をご覧ください。

発展 発展学習  -  アセンブリに属性を適用する

発展学習では意欲的な方のために現段階では特に理解する必要はない項目を解説します。

アセンブリに対して属性をつけるときはAssemblyキーワードを使った少し特殊な書き方をします。

VB2002 VB2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019


<Assembly: System.Reflection.AssemblyCopyright("Visual Basic 中学校")>

この例の AssemblyCopyright属性 (読み方:AssemblyCopyright=アセンブリコピーライト)は、著作権の情報をアセンブリ(≒exe, dll)に付加します。

コンパイラーはビルド時にこの情報を読み取って、exeやdllの著作権情報として埋め込みます。

Windowsでは、exe/dllファイルを右クリックしてプロパティの詳細から著作権情報を確認できます。

exeのプロパティで著作権が表示される

 

アセンブリに対する属性は、どこかのファイルの先頭の方に記述します。後ろの方でも良いのですが、先頭の方に書く人が多いようです。

たとえば、このような場所です。

VB2002 VB2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

Imports System

<Assembly: System.Reflection.AssemblyCopyright("Visual Basic 中学校")>

Module Program

    Sub Main(args As String())
        Console.WriteLine(".NET Core 3.1 Console App")
        Console.ReadLine()
    End Sub

End Module

この例ではプログラムが記述されている中に 属性を記述していますが、属性しか存在しないファイルを作成しても良いです。属性もVisual Basic 言語の一員なので、その場合、拡張子は通常のプログラムと同じ .vb にしてください。

 

アセンブリに対する属性は1プロジェクトで1つしか付けられないので、もし他のどこかに同じ属性が記述されているエラーになります。

Visual Studioが用意しているアプリケーションのテンプレートにはあらかじめアセンブリレベルの属性が組み込まれていることが多いため、自分で記述するとこの理由でエラーになることが多いです。ソリューションエクスプローラーでは、通常アセンブリ向けの属性が組み込まれたファイルは隠されています。

たとえば、Windowsフォームアプリケーションでは、①すべてのファイルを表示をオンにした状態で、My Projectを展開すると、配下に AssemblyInfo.vb というファイルがあることが確認できます。このファイルにアセンブリ向けの属性がいくつか組み込まれています。

アセンブリの属性が記述された隠しファイル

このファイルが隠されているのは、編集する必要がないからです。これらの情報を変更したい場合は、属性の値を直接直すのではなく、プロジェクトのプロパティからアプリケーションタブの「アセンブリ情報」ボタンをクリックします。表示される画面で値を変更すると、隠しファイルに組み込まれている属性の値が連動して変わる仕組みです。

アセンブリ情報の編集

 

2-2.複数の属性

1つの対象に複数の属性を記述することもできます。

次の例では、MyPropプロパティにCategory属性とDescription属性の2つを付けています。

VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

<System.ComponentModel.Category("MyPropプロパティのカテゴリーです。")>
<System.ComponentModel.Description("MyPropプロパティの説明です。")>
Private Property MyProp() As String

属性が多くなると、完全修飾名(= 名前空間名 + クラス名)で記述するのが面倒なので Imports を使うと良いでしょう。この例では、ファイルの先頭の方に Imports System.ComponentModel と記述しておけば、このように少しすっきりした書き方が可能になります。

VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

<Category("MyPropプロパティのカテゴリーです。")>
<Description("MyPropプロパティの説明です。")>
Private Property MyProp() As String

 

複数の属性を適用する別の書き方もあります。 < > の中にカンマで区切って複数の属性を指定することもできます。

VB2010 VB2012 VB2013 VB2015 VB2017 VB2019


<Category("MyPropプロパティのカテゴリーです。"), Description("MyPropプロパティの説明です。")>
Private Property MyProp() As String

ただ、これだとぱっと見たときに、どのような属性が適用されているのかの視認性が悪くなります。1行1属性にしておけば、行頭の方で視線を動かすだけでどういう宣言でどういう属性が適用されているかわかるので視認性に優れています。(つまり、私は、最初に紹介したほうの1行1属性の書き方の方が好みです。)

 

属性を適用する対象も同じ行に記述することができるので次のような書き方もできますが、このように記述されたプロパティやメソッドが大量にあるところを想像するとわかりにくいように感じられる方も多いのではないでしょうか。

VB2002 VB2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019


<Category("MyPropプロパティのカテゴリーです。"), Description("MyPropプロパティの説明です。")> Private Property MyProp() As String

しかし、自動生成されるプログラムではこのような記述はしばしば見かけます。

 

 

3.属性の効果

3-1.プロパティウィンドウ

はじめに説明したように属性自体は単なる目印や追加情報であり、何の機能もありません。

何かのツールやプログラムがその属性がついているもの(変数やメソッドやクラスやアセンブリなど)を処理しようとしたときに、特定の属性がついているか見て、動作を変えることがあります。

例を挙げます。

プロパティウィンドウは、Description属性やCategory属性などの目印が付いているとプロパティウィンドウ上での表示に反映します。ためしに自作のボタンを作ってみましょう。

Windowsフォームアプリケーションで、ソリューションエクスプローラーでプロジェクトを右クリックして、追加 > クラス を選択してください。

プロジェクトにクラスの追加

名前は MessageButton と入力してください。追加するとこのようになります。

MessageButtonクラスが追加された

MessageButtonはボタンをクリックすると設定されたメッセージを表示するようにします。

MessageButton.vb には次のようにプログラムしてください。

VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

Imports System.ComponentModel

Public Class MessageButton
    Inherits Button '←この1行だけで Button と同じ機能になります。あとは追加・変更したい機能だけをプログラムします。

    <Category("動作")>
    <Description("ボタンをクリックしたときに表示されるメッセージ")>
    <DefaultValue("こんにちは!")>
    Public Property Message As String = "こんにちは!"

    Protected Overrides Sub OnClick(e As EventArgs)
        MsgBox(Message, vbInformation)
        MyBase.OnClick(e)
    End Sub

End Class

属性をいちいちフルネームで書くのは面倒なので、この例では System.ComponentModel名前空間を Imports に記述して、この名前空間の記述を省略できるようにしました。

ボタンの作り方は今回のテーマではないので簡単に説明するにとどめます。Inherits Button がポイントで、この1行だけで通常の Button と同じ機能が実現できます。あとは独自に追加・変更したい部分のみをプログラムすればよいだけです。この機能を「継承」(けいしょう)と呼び、VBやC#などのオブジェクト指向言語の強みが発揮させる部分です。

この例では表示するメッセージを表す Message プロパティを 追加しています。 OnClickメソッドはボタンがクリックされたときの動作を制御しており、1行目のMsgBox でメッセージを表示し、2行目の MyBase.OnClick でClickイベントを発生させます。これがないといくらクリックしてClickイベントが発生しないボタンになります。

MyBaseは継承元の機能にアクセスするときに使用するVisual Basicのキーワードです。MyBase.OnClickでClickイベントが発生するのは、継承元のButtonクラスにそのようなプログラムがされているからであって、継承元によってはMyBase.OnClickが存在せずにエラーになったり、呼び出してもClickイベントが発生しないこともありえます。(ありえますが、OnClick という名前は通常Clickイベントを発生させるメソッドに付ける名前なので、継承元クラスが、この名前のメソッドなのにClickイベントが発生しないようなプログラムになっているとすれば相当意地悪です。)

 

このMessageButtonを実際に試すには、この段階で一度ビルドを実行してください。その後Form1.vbのデザイナーに切り替えて、ツールボックスを確認すると、ちゃんとこのMessageButtonを認識して一番上に表示してくれます。Visual Studioはこういった使い勝手がとてもよいです。

ツールボックスにMessageButtonが追加されている

このMessageButtonをクリックして、フォームにぺたっと配置してみましょう。そしてプロパティウィンドウを見ると次のようになっているのがわかります。

プロパティウィンドウの表示に属性が反映されている

属性で指定したとおりに、「動作」に分類されていて、Description属性で指定した説明も表示されていますね。

Messageプロパティの値を何か適当なものに変更してみましょう。たとえば「あああ」にしてみます。実行してボタンをクリックすると、「あああ」を表示されるのがわかります。

実行を終了して、プロパティウィンドウに戻ってこんどはMessageを右クリックしてみてください。

プロパティウィンドウのリセット

リセットという選択肢があります。これをクリックすると、Messageプロパティの値が初期値である「こんにちは!」に戻ります。

プロパティウィンドウはどうやって初期値が「こんにちは!」であることを知るのでしょうか?

DefaultValue属性を見に行くのです。さきほどのプログラムではちゃんとDefaultValue属性に「こんにちは!」と指定してあります。DefaultValue属性がないとプロパティウィンドウは初期値が何か判断できないので、このリセット機能は使用できません。

このように、ここで取り上げた3つの属性はプロパティウィンドウにとってはいろいろと役に立つ属性です。しかし、属性自体には何の機能もないという意味もお分かりいただけるでしょう。

 

3-2.デバッガー

Visual Studioのデバッガーもいろいろな属性で動作が変わります。

上記のプログラムで、MessageButtonのClickイベントで現在の日時を表示するプログラムを次のように追加してみてください。

VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

Public Class Form1
    Private Sub MessageButton1_Click(sender As Object, e As EventArgs) Handles MessageButton1.Click

        MessageButton1.Text = Now.ToString

    End Sub
End Class

End Subの行にF9などでブレークポイントを設定して、実行して、一時停止状態のときに、MessageButton1の上にマウスをホバーさせると次のようにヒントが表示されます。属性を使うとこのヒントを制御できます。

デフォルトのヒント

 

MessageButtonクラスに次のように DebuggerDisplay属性(読み方:DebuggerDisplay=デバッガーディスプレイ)を追加します。

VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

Imports System.ComponentModel

<DebuggerDisplay("属性でヒントを制御")>
Public Class MessageButton
    Inherits Button '←この1行だけで Button と同じ機能になります。あとは追加・変更したい機能だけをプログラムします。

    <Category("動作")>
    <Description("ボタンをクリックしたときに表示されるメッセージ")>
    <DefaultValue("こんにちは!")>
    Public Property Message As String = "こんにちは!"

    Protected Overrides Sub OnClick(e As EventArgs)
        MsgBox(Message, vbInformation)
        MyBase.OnClick(e)
    End Sub

End Class

 

この状態で同じように実行してホバーさせると DebuggerDisplay属性で指定した値が表示されます。デバッガーはここにヒントを表示するときに、対象の DebuggerDisplay属性を見に行くようになっているというわけです。

DebuggerDisplay属性でヒントを制御

 

DebuggerDisplay属性の値には { } を使ってそのクラスのメンバーを指定できるようになっているので、次のような指定もできます。

VB2010 VB2012 VB2013 VB2015 VB2017 VB2019


<DebuggerDisplay("「{Message}」を表示するボタンです。Textは{Text}です。")>

このように指定すると、表示は次のようになります。

DebuggerDisplay属性でメンバーの値を表示

 

この、"「{Message}」を表示するボタンです。Textは{Text}です。" という文字列を解釈する機能は デバッガーの機能であり、Visual Basicや.NETの機能ではありません。何度も言いますが属性自体には何の機能もないのです。属性を目印にして、デバッガーが特別な動作を実行しているわけです。

 

3-3.XMLシリアライズ

ここまでは開発時に使用するプロパティウィンドウやデバッガーが使用する属性を説明しました。実行時に動作するツールや機能でも属性を見に行って動作が変わるものが存在します。

一例としてXMLシリアライズを取り上げます。XMLシリアライズとは、クラスや構造体の内容をXMLの文字列として表現する機能です。XMLとはHTMLのようなタグを使ってデータを表現する方式です。

この逆を行うXMLデシリアライズと組み合わせるとクラスや構造体の状態を文字列としてファイルに保存して、その保存されたものから元のクラスや構造体を復元することができます。

 

フォームにButtonを1つ配置して、次のプログラムを記述してみてください。

VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

Imports System.Xml.Serialization

Public Class Form1

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click

        Dim p As New Person
        p.Name = "徳川家康"
        p.Age = 24
        p.Memo = "江戸幕府を開いた人"

        Dim fileName As String = AppDomain.CurrentDomain.BaseDirectory & "person.xml"

        SerializePerson(fileName, p)

        MsgBox(fileName & "に保存しました。")

    End Sub

    ''' <summary>
    ''' personを fileName で指定したファイルに保存します。
    ''' DeserializePersonメソッドを使うと保存したファイルから Person を復元できます。
    ''' </summary>
    Private Sub SerializePerson(fileName As String, person As Person)

        Using stream As New IO.FileStream(fileName, IO.FileMode.Create)
            Dim serializer As New XmlSerializer(GetType(Person))
            serializer.Serialize(stream, person)
        End Using

    End Sub

End Class

Public Class Person
    Public Property Name As String
    Public Property Age As Integer
    Public Property Memo As String
End Class

これを実行してボタンをクリックすると、徳川家康の情報を保持しているPersonクラスがXMLファイルとして保存されます。保存されたファイルのパスはメッセージに表示するようにしてあります。

このファイルをメモ帳など開くとこのようになっています。XMLを始めてみた人でも情報が保存されているのがわかりますよね。

<?xml version="1.0"?>
<Person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <Name>徳川家康</Name>
    <Age>24</Age>
    <Memo>江戸幕府を開いた人</Memo>
</Person>

 

このようにクラスや構造体の情報をXMLにする XmlSerializerクラス (読み方:XmlSerializer=エックスエムエルシリアライザー)は、XMLを生成するときに対象のクラスやプロパティについている属性を探して、属性がついていると既定の動作を変更させます。

PersonクラスのAgeプロパティに次のようにXmlIgnore属性 (読み方:XmlIgnore=エックスエムエルイグノア)を付けてみましょう。

※このように単なるデータとして定義されているプロパティが並んでいるだけの場合、属性とプロパティを同じ行に書いたほうが見やすいこともあると私は思っており、改行なしで属性を記述することがあります。

VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

Public Class Person
    Public Property Name As String
    <XmlIgnore> Public Property Age As Integer
    Public Property Memo As String
End Class

XmlIgnore属性をつけるとXmlSerializerクラスはそのプロパティを無視します。結果として、できあがるファイルは次のように Age を含まないものになります。

<?xml version="1.0"?>
<Person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <Name>徳川家康</Name>
    <Memo>江戸幕府を開いた人</Memo>
</Person>

 

Viual Basicの言語としてはクラスやメソッドを呼び出し時の動作は引数で決まるのが一般的ですが、このように属性によって動作が変わる機能もあるということです。この例の場合、「Ageプロパティを無視する」ということを引数で伝えようとすると確かに大変で、属性のような別の方法でそれを制御するということは合理的です。この場合でも属性自体には何の機能もないという原則は維持されており、Ageプロパティも他のプロパティと同様に読み書きできます。

 

以上では、属性を利用するツールや機能の例として、プロパティウィンドウ、デバッガー、XMLシリアライズの3つをとりあげましたが、これにとどまらずいろいろなツールや機能が属性を利用しています。

 

発展 発展学習  -  XMLファイルからPersonを復元する

発展学習では意欲的な方のために現段階では特に理解する必要はない項目を解説します。

本文で紹介した例は Personクラスの内容をXMLファイルに保存するものでした。こうして保存されたXMLファイルからPersonクラスを復元するには次のようにします。

VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

''' <summary>
''' fileNameで指定したファイル から Person を復元します。
''' </summary>
Private Function DeserializePerson(fileName As String) As Person

    Using stream As New IO.FileStream(fileName, IO.FileMode.Open)
        Dim deserializer As New XmlSerializer(GetType(Person))
        Dim result As Person = DirectCast(deserializer.Deserialize(stream), Person)
        Return result
    End Using

End Function

Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click

    Dim fileName As String = AppDomain.CurrentDomain.BaseDirectory & "person.xml"
    Dim p As Person = DeserializePerson(fileName)

    MsgBox($"{p.Name} {p.Age}歳 メモ:{p.Memo}")
    'MsgBox(String.Format("{0} {1}歳 メモ:{2}", p.Name, p.Age, p.Memo)) '←VB2013以前の場合

End Sub

ファイルに保存した内容はプログラムを終了したり、Windows/Linuxを終了させても残るので、これを応用すればセーブ/ロード機能のあるアプリケーションを作成できます。

 

4.呼び出し元情報属性

数ある属性の中でもVB2012の時に追加された下記3つの属性は特色あるので紹介しておきます。

これらの属性は引数に付けます。

この例ではファイルの先頭の方に Imports System.Runtime.CompilerServices が記述されていることが前提です。

VB2012対応 VB2013対応 VB2015対応 VB2017対応 VB2019対応

Public Sub InvokeTest()
    Me.Test
End Sub

Public Sub Test(<CallerMemberName> Optional callerMethodName As String = "",
                <CallerFilePath> Optional callerFilePath As String = "",
                <CallerLineNumber> Optional callerLineNumber As Integer = -1)

    Debug.WriteLine($"{callerFilePath}ファイルの{callerMethodName}{callerLineNumber}行目から呼び出されました。")

End Sub

Debug.WriteLineが表示される場所

この属性がついた引数は Optional になっており、呼び出し時に値を指定する必要がありません。

Optional がついている引数は省略可能で、省略されたときの既定値を上の例のように指定しておく必要があります。コンパイラーはこの既定値を見て、コンパイル時に呼び出し元に自動的にこの値を補うわけです。

つまり、属性が付いていない場合、プログラムは Me.Test という何の引数もついていない呼び出しなのに、コンパイラーは次の呼び出し出るようにコンパイルします。

Me.Test("", "", -1)

呼び出し元属性がついているとき、コンパイラーは既定値の代わりに下記のような特別な情報を埋め込みます。

結果として、この例では「C:\Users\rucio\Source\Repos\ConsoleApp1\TestClass.vbファイルのInvokeTestの7行目から呼び出されました。」のように表示されます。

プログラムの動作や調べるときにファイル名や行番号やメンバー名を取得できるのはなかなか便利です。

注意点としては、この値はコンパイラーによって埋め込まれるのでコンパイルを実行した時点の情報が埋め込まれウということです。たとえば、あなたのパソコンでコンパイルを実行した場合、あなたのパソコン上でのフォルダー名が埋め込まれます。フォルダー名にはユーザー名等の個人情報を含む場合があります。

 

メンバー名については、コンパイラーはVBのプログラム上のメンバー名ではなく、通常フレームワークによって隠されている自動生成されるメンバーの名前を埋め込みます。

メソッドの場合は、プログラム上の名前と一致しますが、コンストラクター(Sub New)の場合、「.ctor」という名前で埋め込まれるなど下記表のような違いが出ます。

種類 取得する名前
メソッド(Function, Sub) メソッド名。
プロパティ(Property) get_プロパティ名 または set_プロパティ名。例:get_Enabled
コンストラクター(Sub New) .ctor
静的コンストラクター(Shared Sub New) .cctor
演算子(Operator) op_演算子名。例:op_Addition

参考

https://docs.microsoft.com/ja-jp/dotnet/visual-basic/programming-guide/concepts/caller-information