Visual Basic 初級講座 [改訂版] |
Visual Basic 中学校 > 初級講座[改訂版] >
最終更新日 2020/8/12 公開 2020/8/9
改訂履歴
この記事が対象とする製品・バージョン
Visual Basic 2019 | ◎ | 対象です。 | |
Visual Basic 2017 | ◎ | 対象です。 | |
Visual Basic 2015 | ◎ | 対象です。 | |
Visual Basic 2013 | ◎ | 対象です。 | |
Visual Basic 2012 | ◎ | 対象です。 | |
Visual Basic 2010 | ◎ | 対象です。 | |
Visual Basic 2008 | ◎ | 対象です。 | |
Visual Basic 2005 | ◎ | 対象です。 | |
Visual Basic.NET 2003 | △ | 対象外ですがほとんどの説明があてはまるので参考になります。 | |
Visual Basic.NET (2002) | △ | 対象外ですがほとんどの説明があてはまるので参考になります。 | |
Visual Basic 6.0 | × | 対象外です。 |
目次
IntegerやStringなどの基本型を使うと単一のデータ(値)を保持することができますが、実際にある程度複雑なプログラムを扱っているとデータは意味のあるまとまりとして扱うシーンが多くなります。データの組み合わせに意味があったり、あるいは「住所」というもっと高度なデータを扱うこともよくあります。VBには住所というデータ型はありませんが、住所は分解すると都道府県・市区町村・番地・郵便番号など基本的な型で表現できる要素の組み合わせ出ることがわかります。
Visual Basicにはこのようなデータの構造を表現することができる機能がいくつかあります。
基本型で表現する変数を大量に扱うようにプログラムすれば、このようなデータを構造化する機能は不要なのですが、あまりにもプログラムが煩雑になってしまい現実的ではありません。また、フレームワークの便利な機能などを使用しようとするとデータが構造化されていることが前提となっていることもしばしばです。どのように構造できるのか見ておくことにしましょう。今回の説明はリファレンス的な説明であり、暗記する必要はありません。必要になったときに戻ってきて見直せばよいのです。だから、あまり方に力を入れないでざっと読むことをお勧めします。
タプル(Tuple)を使うと1つの変数に複数の値(要素)の組み合わせを持つことができます。
タプルはVisual Basic 2017で追加された新しい機能です。
次の例はタプルを使って1つの変数に文字と数値の値(要素)を持たせています。
Dim x = ("あいうえお",
12345) Debug.WriteLine(x.Item1) 'あいうえお と表示されます。 Debug.WriteLine(x.Item2) '12345 と表示されます。 |
このようにタプルは複数の値(要素)をカンマで区切ってカッコの中に入れて表現します。
それぞれの値(要素)を読み書きするには Item1フィールド、Item2フィールドを使用します。
同じように3つの値を表現することもできます。
Dim x = ("あいうえお",
12345, "ABC") Debug.WriteLine(x.Item1) 'あいうえお と表示されます。 Debug.WriteLine(x.Item2) '12345 と表示されます。 Debug.WriteLine(x.Item3) 'ABC と表示されます。 |
4つでも、5つでも同じことができます。
上記の例では変数xの型宣言を省略しています。タプルを使う場合は、通常、型推論機能を使って自動的に型を判定させるのでこのように書きますが、あえて省略せずに型を記述すると、最初の例ではx は ValueTuple(Of String, Integer)型になり、2番目の例では、ValueTuple(Of String, Integer, String)型になります。つまり、タプルの実体はフレームワークの ValueTuple構造体(読み方:ValueTuple=バリュータプル)であり、このValueTuple構造体はどのような型の組み合わせ表現できるように、(コレクションと同じように)ジェネリック機能を使った型パラメーターをとるというわけです。
コレクションではListの場合、型パラメーターが1つ、Dictionaryの場合、型パラメーターが2つと決まっています。List(Of String) や Dictionary(Of String, Integer)などです。 ValueTupleはフレームワーク側で型パラメーターの数に応じた複数の定義が用意されているのと、Visual Basicの言語機能に統合されて、専用の構文で扱えるのとで、うまい具合に、型パラメーターが可変であるように見せかけています。実体としては値が8個以上あるタプルでは、8個目の以降の値は別のタプルにまとめられ、タプルを持つタプルになります。しかし、Visual Basicはこれをうまく隠蔽し、Item9, Item10などの名前でアクセスできるようにするので、使っている側からするといくつでも値を持てるように見えます。 |
タプルはプログラムがわかりにくくなるので基本的には使わない方がよいです。複数の値が必要ならば、複数の変数を用意するのがよいのです。
タプルが役に立つのは複数の戻り値を返すメソッドを作成するときだけです。メソッドの戻り値は1つの変数で表現できるものである必要があるので、ここではどうしても変数が1つしか使用できません。Visual Basic 2017より前は、メソッドから複数の値を戻す必要がある場合、後で説明する構造体やクラスを使って表現するのが良いとされていました。しかし、これだとちょっと小さな機能を作成するときにもいちいち構造体やクラスを作成しなければならないときがあり、不便でした。タプルはこの悩みを解消します。メソッドの作り方は次回説明します。
既定ではタプルの要素を読み書きするにはItem1、Item2、・・・というわかりにくい名前のフィールドを使用することになります。
タプル作成時に要素に名前を付けることでもっとわかりやすい名前でアクセスさせることもできます。
次のように := を使った少し変わった構文を使用します。
Dim ieyasu = (Name:="徳川家康",
Age:=23) Debug.WriteLine(ieyasu.Name) '徳川家康 と表示されます。 Debug.WriteLine(ieyasu.Age) '23 と表示されます。 |
このプログラムでは変数ieyasuはValueTuple(Of String, Integer)型であり、フィールドはItem1、Item2、・・・という名前なのですが、Visual Basicの言語機能により、Nameという記述でItem1フィールドにアクセスし、Ageという名前でItem2フィールドにアクセスできるようになります。
Visual Studioは ieyasu. と入力するとヒントに Item1、Item2は表示せず代わりに Name、Ageを表示するようになるので、まるでそのようなオブジェクトが存在するかのような使いやすさでタプルを扱うことができます。
ブレークポイントをしかけて、変数ieyasuを右クリックしてクイックウォッチをするとどのように値を持っているかわかります。Visual Basicは Name や Age というフィールドがあるように見せかけていますが、「Raw View」を展開すると、実際にはItem1とItem2というフィールドがあるということがわかります。
つまり、実際にはフィールドの名前を変える機能など存在しないのに、Visual Basicはフィールドの名前が変わっているように見せかけています。実際に、プログラマーからすると本当にフィールドの名前が変わっているのか、Visual Basicがそう見せかけているだけなのかは気にしなくて良いレベルです。 このように実際にそのような機能があるわけではないのに、コンパイラ等の表層の働きでそのような機能があるかのように書ける構文を糖衣構文(とういこうぶん)またはシンタックスシュガーと呼びます。苦い部分を甘い層でコーティングしているというわけです。 |
さらに、Visual Basicは上記の例のように明示的に名前を指定しなくても、使用されている変数などからタプルの要素の名前を推論することもできます。
次の例ではタプル作成時に使用している変数の名前から自動的にタプルの要素に名前が付きます。
Dim Name
As String = "徳川家康" Dim Age As Integer = 23 Dim ieyasu = (Name, Age) Debug.WriteLine(ieyasu.Name) '徳川家康 と表示されます。 Debug.WriteLine(ieyasu.Age) '23 と表示されます。 |
この機能はVisual Basic 2017でも、マイナーバージョン 15.3以上で使用できます。
変数ではなく、タプル作成時にメソッドやプロパティを使用している場合、その名前から要素名を推論します。
Dim ieyasu = (Environment.UserName, Now.Hour) |
この例では、要素名は UserName と Hour になります。
ToStringやItem1など 名前が衝突する場合などうまく推論できない場合もあります。
このタプルの要素名の推論は一見便利そうに見えますが、メソッドの戻り値になる場合以外、タプル自体の使用を推奨しないので、あまり意味はありません。メソッドの戻り値として便利にタプルを使用する方法は次回説明します。
ここまではVisual Basicのタプルの記述方法を紹介し、タプルの実体がフレームワークのValueTuple型であることも説明しました。
フレームワークにはもう1つ、その名もずばり Tuple (読み方:Tuple=タプル)というクラスがあります。Visual Basicのタプルと同じような使い方ができるのですが、Visual Basicの構文としてはサポートされておらず、このTupleクラスを使いたい場合は、一般のフレームワークのクラスとして呼び出す必要があります。
たとえば、次のようにしてTupleクラスを使用できます。
(Of String, Integer)は型パラメーターで、その後の("徳川家康", 23)はコンストラクターの呼び出しです。
Dim ieyasu
As New Tuple(Of
String, Integer)("徳川家康",
23) Debug.WriteLine(ieyasu.Item1) '徳川家康 と表示されます。 Debug.WriteLine(ieyasu.Item2) '23 と表示されます。 |
このプログラムを見るとわかるように、これができても嬉しいことはほとんどなく、プログラムもわかりにくいです。Visual Basicのタプルと比べると古いバージョン(VB2010)から使えるところくらいがしいて挙げられるメリットのように見えます。実際に使っている人はほとんどいません。
微妙な違いはいくつかあって、たとえば、Visual Basicのタプルの実体であるValueTupleは構造体で、Tupleはクラスです。TupleではItem1、Item2はフィールドではなく、プロパティです。Tupleでは、Item1、Item2の値を変更することはできません。
こういったことはまったく覚える必要はありません。似たようなものがもう1個あるんだな、何か微妙な違いがあるんだなというところだけ覚えておいてください。
構造体は、タプルのように、変数の宣言時に型や項目の名前を指定するのではなく、あらかじめ名前をつけて定義しておくことができます。
たとえば、次のようにNameフィールドとAgeフィールドを持つPerson構造体を定義できます。
構造体を定義するには Structureキーワード(読み方:Structure=ストラクチュア)を使用します。
Public Structure
Person Public Name As String Public Age As Integer End Structure |
この定義を書ける場所はプロシージャの外側です。
今まで初級講座で出てきたプログラムはほとんどが、Sub ~ End Sub の内側に書いていたと思います。
たとえば、次のようなプログラムです。
Private Sub
Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click MsgBox("Hello! World") End Sub |
構造体の定義はこの外側に書きます。
Windowsフォームアプリケーションを例にすると、たとえば、下の図で 示している場所です。
Button1_Clickより上側に書いても構いません。
いっそ、End Classより下に書いても構いません。
Class ~ End Classのなかに構造体の定義を書くと、その構造体はそのクラスのメンバーになります。外に書くと、構造体はそのクラスとは無関係になります。基本的にはクラスの外側に書いたほうが扱いやすいです。
Visual Basicとしてはクラスより上(Public Class Form1より上)に構造体の定義を書いても何の問題もないのですが、Visual Studio的には問題があるかもしれません。 はっきり裏を取れていないのですが、確か古いVisual Studioは、Formのプログラムより上に構造体の定義があるとフォームデザイナーが機能しなくなったように記憶しています。Visual Studio 2019で試したところ特に問題なく動いていたので、もしかしたら記憶違いかもしれません。 |
なお、この例ではNameやAgeはフィールドになります。VB2010以上であればキーワード Property (読み方:Property=プロパティ)を付けるとプロパティにすることができます。
Public Structure
Person Public Property Name As String Public Property Age As Integer End Structure |
このようにPropertyキーワードを付けるだけで簡単にプロパティを定義する構文を「自動実装プロパティ」と呼びます。 VB2008以前の場合、プロパティを定義するにはもう少し複雑な構文を使用する必要があります。それは別の機会に説明します。
構造体は「型」です。型として使用します。
Dim ieyasu
As New Person ieyasu.Name = "徳川家康" ieyasu.Age = 23 Debug.WriteLine(ieyasu.Name) '徳川家康 と表示されます。 Debug.WriteLine(ieyasu.Age) '23 と表示されます。 |
型のよいところは再利用可能なところです。いつでもAs New Personと宣言することでこの型を利用することができます。
タプルは(Item1,Item2,・・・という名前が嫌なら)毎回名前をつける必要があるので、何度も同じようなタプルをプログラムするようなら構造体の方が楽です。逆に1箇所でしか使わないのならあらかじめ定義しないで使えるタプルの方が楽かもしれません。
なお、構造体はクラスではないので New はつけてもつけなくても同じです。ただ、Newがないと、後でプログラムを見直したとき New の付け忘れミスかと勘違いしたり(クラスであればNewが必須なので紛らわしいんです)、Visual Studioが警告を表示したりするので不要であってもNewは必ず付けるようにしておくと面倒がなくてよいです。
ところで、IntegerやLong, Dateなどの基本型も実体は構造体であるということを覚えているでしょうか? 構造体は常に値自体を現しているため、それぞれがインスタンスです。だから、クラスのように New を使ってインスタンスを作成する必要はありません。自作の構造体も同じです。前の例でAs New Personではなく、As Personと宣言しているのはそれだけでインスタンスが作成されるからです。 でも、クラスと構造体を区別するのが面倒な人のために New を使っても良いことになっています。つまり、前の例を As New Person に書き換えても結果は同じです。 |
構造体は New 型名 With { } という特殊な構文を使って初期化することもできます。この構文を使用する場合は New が必須です。
Dim ieyasu
As New Person
With {.Name =
"徳川家康", .Age = 23} Debug.WriteLine(ieyasu.Name) '徳川家康 と表示されます。 Debug.WriteLine(ieyasu.Age) '23 と表示されます。 |
この初期化の構文で { } の中で . (ドット) を入力すると Visual Studioはちゃんと定義済みのメンバーを候補で表示してくれるので入力は楽です。
この構文はクラスの初期化でも使用できます。
VB2010以上だと記号なしで改行ができるので、次のように書くこともあります。
Dim ieyasu
As New Person
With { .Name = "徳川家康", .Age = 23 } Debug.WriteLine(ieyasu.Name) '徳川家康 と表示されます。 Debug.WriteLine(ieyasu.Age) '23 と表示されます。 |
構造体は自分で定義するので、さまざまな定義が可能で、たとえば、読み取り専用プロパティや、定数、メソッドやコンストラクターを定義することも可能です。具体的な記述方法は別の機会に説明します。これらの記述方法はクラスでも構造体でも同じなので、クラスの記述方法について説明している内容は構造体にもおおむね当てはまります。
構造体はフレームワークの中にも大量に用意されています。
Visual Studio 2019では入力後方の中で青い四角を2つ重ねたようなアイコンで表示されるのが構造体です。As + スペースを入力して、候補を眺めるとちらほら構造体があるのがわかります。
フィルターで構造体だけの一覧にすることもできます。
もっとも簡単な構造体は座標を表す Point構造体です。Point構造体はX座標とY座標を表す、XプロパティとYプロパティを持ちます。
似たようなものに PointF 構造体があります。これも座標を表し、XプロパティとYプロパティを持っていますが、座標が整数(Integer)ではなく、小数(Double)であるところが違いです。 |
WindowsフォームアプリケーションではPoint構造体を利用してフォームのLocationプロパティに設定することで表示位置を指定できます。
Dim pos
As New Point pos.X = 100 pos.Y = 200 'フォームの位置を(100, 200)にします。 Me.Location = pos |
VB2008以上であればこのプログラムを構造体の初期化の構文を使って書き換えることもできます。
Dim pos
As New Point
With {.X = 100, .Y = 200} 'フォームの位置を(100, 200)にします。 Me.Location = pos |
しかし、フレームワークに用意されている定義はよく考えられていて使いやすくできています。Point構造体にはXプロパティとYプロパティを指定できるコンストラクターが用意されており、次のようにもっと楽に書けます。
Dim pos
As New Point(100, 200) 'フォームの位置を(100, 200)にします。 Me.Location = pos |
上記のプログラムをさらに簡略化して次のように書くこともできます。
よく考えてみると Locationプロパティ自体がPoint型なのだら、次のように書いてもいいように思うかもしれません。 が、これは間違いです。
これがなぜダメからは、初級講座第7回 クラスを使う仕組み で説明しています。(けど、ほとんどの読者は忘れていると思うので、ここで改めて注意喚起します) |
ちょっと、ややこしいのですが、New 型名 With { } という初期化の構文は「型名」の部分を省略して書くことができます。
たとえば、次のようになります。
Dim ieyasu
As New With { .Name = "徳川家康", .Age = 23 } Debug.WriteLine(ieyasu.Name) '徳川家康 と表示されます。 Debug.WriteLine(ieyasu.Age) '23 と表示されます。 |
この場合、型名が記述されていないので変数ieyasu は Person型 にはなりません。また、事前にPerson型を宣言しておく必要もありません。
NameとAgeがある型であることは宣言を見ればわかるので、Visual Basicが自動的に逆算的に型を作成して用意してくれるのです。この型には名前がついていないことから匿名型(とくめいがた)と呼ばれます。構造体なのかクラスなのかで区別すればクラスです。
Visual Basicが自動的に作成した型の名前が知りたい場合は、GetType.Name を使用します。
Dim ieyasu
As New With
{.Name = "徳川家康", .Age =
23} Debug.WriteLine(ieyasu.GetType.Name) |
私の環境で実行すると、VB$AnonymousType_0`2 と表示されました。どのような名前がつくかはVBにお任せで、いつも同じ名前が付く保証はありません。だから、このような学習上の興味で名前を確認する以外、名前を知ることに意味はありません。
それにしても、この匿名型と構造体とタプルは似たようなことが表現できて紛らわしいです。あまりそれぞれの違いを探したり、使いどころを検討するのではなく、必要なときがきたら必要なものを使うというスタンスでよいのですが、次の章で、他のデータ構造の復習も踏まえてざっと概観してみましょう。
さて、これでVisual Basicでのデータの表現形式が一通り登場しました。少し振り返って全体を俯瞰してみましょう。
まずは単一のデータを表現する基本型(またの名をプリミティブ型)です。これを使うと1つの値を保持しておくことができました。基本型には整数を表現するIntegerやLong,小数も表現できるDecimalやDouble、文字列を表現するString,日付を表現するDateなどがありました。Visual BasicやC#など.NETでは他のもっと複雑な型でもデータに関してはすべてこの基本型の組み合わせから成り立っています。
Dim name
As String = "徳川家康" Dim age As Integer = 23 Debug.WriteLine(name) '徳川家康 |
タプルを使うと複数の値の組み合わせを保持しておくことができました。
Dim ieyasu = ("徳川家康",
23) Debug.WriteLine(ieyasu.Item1) '徳川家康 |
タプルの各項目には名前を付けることもできました。
Dim ieyasu = (Name:="徳川家康",
Age:=23) Debug.WriteLine(ieyasu.Name) '徳川家康 |
匿名型は名前付きタプルと同じように使うことができます。タプルの場合は、各項目はフィールドですが、匿名型ではプロパティになります。タプルはTuple型ですが、匿名型は自動的にVBが作成する機械的な名前の型になります。名前付きタプルを使うか匿名型を使うか迷うようなら好きなほうを使えばよいです。私の経験ではどちらもあまり使いません。
Dim ieyasu
As New With { .Name = "徳川家康", .Age = 23 } Debug.WriteLine(ieyasu.Name) '徳川家康 と表示されます。 Debug.WriteLine(ieyasu.Age) '23 と表示されます。 |
匿名型ではVBが自動的に対応する型を作成して命名してくれますが、そうではなく、自分で型を定義するのが構造体です。構造体を定義するには Structure キーワードを使用します。一度定義しておけば匿名型やタプルと違っていろいろなところで使いまわしできるので使い勝手がよく、私は匿名型やタプルよりも構造体を使うことが多いです。
Public Sub Test() Dim ieyasu As New Person ieyasu.Name = "徳川家康" ieyasu.Age = 23 Debug.WriteLine(ieyasu.Name) '徳川家康 と表示されます。 Debug.WriteLine(ieyasu.Age) '23 と表示されます。 End Sub Public Structure Person Public Property Name As String Public Property Age As Integer End Structure |
それから、 構造体でも New 型名 With { } という特殊な構文を使って簡単に値を初期化する構文も用意されているのでした。
Dim ieyasu
As New Person
With {.Name =
"徳川家康", .Age = 23} Debug.WriteLine(ieyasu.Name) '徳川家康 と表示されます。 Debug.WriteLine(ieyasu.Age) '23 と表示されます。 |
配列を使うと同じ型の値をたくさん表現することができました。タプル・匿名型・構造体と異なり、『同じ』型の値を複数持てるのが特徴です。
Dim names
As String() = {"徳川家康",
"豊臣秀吉",
"織田信長",
"三好長慶"} Debug.WriteLine(names(0)) '徳川家康 |
コレクションのListを使うと配列と同じように同じ型の値をたくさん表現できるうえに、後から値を追加削除するのも簡単でした。
Dim names As New List(Of String) From {"徳川家康", "豊臣秀吉", "織田信長", "三好長慶"} Debug.WriteLine(names(0)) '徳川家康 |
Listの初期化にはこの例のように From { } という特殊な構文が使用できるのでした。
コレクションのDictionaryを使うとキーを付けて値の読み書きができました。
Dim names
As New
Dictionary(Of String,
String) From
{{"江戸",
"徳川家康"}, {"桃山",
"豊臣秀吉"}, {"安土",
"織田信長"}, {"室町",
"三好長慶"}} Debug.WriteLine(names("江戸")) '徳川家康 |
Dictionaryの初期化にはこの例のように From {{ }} という特殊な構文が使用できるのでした。
以上をまとめると、プログラミング言語 Visual Basicの機能でデータを保持する仕組みは次のようになります。
考え方 | 実体 | 初登場 | 私は | 記述方法の例 | |
---|---|---|---|---|---|
基本型 (プリミティブ型) |
単一の値 | As で宣言する型 /VBが推論した型 |
VB1 (1991年) |
とてもよく使う | Dim x As Integer = 123 |
タプル | 値の組み合わせ | ValueTuple構造体 | VB2017 (2017年) |
まず使わない | Dim x = ("A", 123) または Dim x = (xxx:="A",yyy:=123) |
匿名型 | 構造化されたデータ | VBが逆算して自動生成したクラス | VB2008 (2007年) |
まず使わない | Dim x = New With { .xxx = "A", .yyy = 123} |
構造体 | 構造化されたデータ /自作のデータ型 |
自作の構造体 | VB4以前? (1995年以前?) |
ときどき使う | Public Structure zzz Public Property xxx As String Public Property yyy As Integer End Structure ------- Dim x As New zzz With {.xxx = "A", .yyy = 123} |
配列 | 複数の値 | 配列(Arrayクラス) | VB1 ((1991年) |
ときどき使う | Dim x = {1, 2, 3, 4} |
コレクション | 複数の値 | ListやDictionaryなどのクラス | VB5以前? ((1997年以前?) |
よく使う | Dim x As New List(Of Integer) From {1, 2, 3, 4} |