2013-12-20T00:56:56の更新内容

programming/vb.net/basics/12_class/index.wiki.txt

current previous
1,13 1,10
 
${smdncms:title,クラス(Class)}
${smdncms:title,クラス(Class)}
~
${smdncms:keywords,クラス,Class,インスタンス,継承,派生,インターフェイス,実装,値型,参照型}
${smdncms:keywords,クラス,Class,値型,参照型}
+

          
 
#navi(..)
#navi(..)
~

          
クラスとは、簡単に言うと構造体の様に任意数のデータ型を組み合わせた複合データ型で、それらのデータに対して処理を行う手続き(メソッド)を持つもの、それがクラスです。 クラス(Class)は「学級・教室」などの意味ではなく、「階級・分類」の方の意味です。
+
クラスとは、簡単に言うと[[構造体>programming/vb.net/basics/09_structure]]のようにいくつかのデータを持ち、それらのデータに対して処理を行う手続き([[メソッドもしくはプロシージャ>programming/vb.net/basics/07_procedure]])も合わせ持つ''複合データ型''です。 ここで言うクラス(Class)は「学級・教室」などの意味ではなく、「種類・分類・階級」の意味の方を表します。
 

        

        
 
-関連するページ
-関連するページ
 
--[[programming/vb.net/basics/09_structure]]
--[[programming/vb.net/basics/09_structure]]
+
--[[programming/vb.net/basics/11_accessibility]]
 
--[[programming/vb.net/me_myclass_mybase]]
--[[programming/vb.net/me_myclass_mybase]]
 
--[[programming/vb.net/generics]]
--[[programming/vb.net/generics]]
 
--[[programming/netfx/valuetype_referencetype]]
--[[programming/netfx/valuetype_referencetype]]
18,827 15,306
 

        

        
 
#googleadunit(banner)
#googleadunit(banner)
 

        

        
~
*VB.NETにおけるクラス
*VB.NETでのクラス
~
VB.NETにおけるクラスは多くの部分で構造体と類似していますが、両者の違いをまとめると次のようになります。
VB.NETにおけるクラスの機能は多くの部分が構造体と似ていますが、その違いをまとめると次の通りです。
 

        

        
~
-クラスは参照型であり、構造体は値型である ([[programming/netfx/valuetype_referencetype]])
-クラスは参照型であり、構造体は値型である
+
-クラスはファイナライザ(デストラクタ)を実装できるが、構造体はできない ([[programming/netfx/disposing]])
 
-クラスは継承を行うことができるが、構造体はできない
-クラスは継承を行うことができるが、構造体はできない
-
-クラスはファイナライザ(デストラクタ)を実装できるが、構造体はできない
 

        

        
~
このような特徴はVB.NETに固有のものではなく、C#など他の.NET Frameworkの言語とも共通するものとなっています。
このうち、もっとも重要なのが継承をサポートしているのがクラスだけである点です。 これらの違いについては追って説明していきます。
 

        

        
 
*クラスの宣言
*クラスの宣言
~
一つの``cls``ファイルに一つのクラスを記述していたVB6以前とは異なり、VB.NETでは``Class``キーワードを使ってクラス宣言します。 宣言の例は次の通りです。
VB6以前とは異なり、クラスは一つのclsファイルに記述するのではなく、Classキーワードを使って宣言します。 つまり、[[モジュール>programming/vb.net/basics/08_module]]等と同様に一つのファイルに任意個のクラスを宣言することができます。 宣言の例は次の通りです。
+

          
 
#code(vb){{
#code(vb){{
~
' Accountクラスの宣言
Class Person
+
Class Account
+
  ' IDを格納するフィールド
+
  Public ID As Integer
+
  ' 名前を格納するフィールド
+
  Public Name As String
+
End Class
+
}}
+

          
+
クラスの宣言は``Class``キーワードを用いて宣言し、``End Class``までがクラス宣言になります。 その間でフィールド(メンバ変数)、プロパティ、メソッドなどを宣言します。 フィールドなどクラスのメンバには``Public``などの[[アクセシビリティ>programming/vb.net/basics/11_accessibility]]を指定することができます。
 

        

        
~
また、[[構造体>programming/vb.net/basics/09_structure]]や[[モジュール>programming/vb.net/basics/08_module]]と同様に、クラスも一つのファイルに任意個のクラスを宣言することができます。 クラスと構造体を一つのファイルでまとめて宣言することもできます。
    Public Age As Integer
-
    Public Name As String
 

        

        
+
#code(vb,sample.vb){{
+
' クラスC1の宣言
+
Class C1
+
End Class
+

          
+
' クラスC2の宣言
+
Class C2
 
End Class
End Class
-
}}
 

        

        
~
' 構造体S1の宣言
クラスの宣言はClassキーワードを用いて宣言し、End Classまでがクラス宣言になります。 その間でフィールド、プロパティ、メソッドなどを宣言します。
+
Structure S1
+
End Structure
+
}}
 

        

        
 
*インスタンス
*インスタンス
~
構造体とは異なり、クラスはクラス型変数を宣言しただけでは使用できません。 ''インスタンス''を作成して宣言した変数に代入することで初めてクラスを使えるようになります。
クラスは構造体とは違ってクラス型変数を宣言しただけでは使用できません。 宣言した変数にインスタンスへの参照を代入することで初めて使える様になります。
 

        

        
~
Classキーワードによって宣言したクラスはただの''定義''であり、それを''実体化''したものがインスタンスであり、インスタンスを作成することを「クラスをインスタンス化する」といいます。 クラスとは設計図であり、インスタンスとはその設計図を元に作られた個々の製品であるとイメージすればわかりやすいかもしれません。
インスタンスとは、定義から実体化されたクラスのことで、インスタンスを作成することを「クラスをインスタンス化する」ともいいます。 クラスとは設計図にすぎず、インスタンスとはその設計図を元に作られた個々の製品であるとイメージすればわかりやすいかもしれません。
 

        

        
 
クラスとクラス型変数、インスタンスの関係を次のコードを使って説明します。
クラスとクラス型変数、インスタンスの関係を次のコードを使って説明します。
 

        

        
 
#code(vb){{
#code(vb){{
~
Imports System
Class Person
-

          
-
    Public Age As Integer
-
    Public Name As String
 

        

        
+
' クラス(宣言)
+
Class Account
+
  Public ID As Integer
+
  Public Name As String
 
End Class
End Class
 

        

        
~
Module Sample
Module MainModule
~
  Sub Main()

          
~
    Dim alice As Account ' クラス型変数
    Sub Main()
~

          

          
~
    alice.ID = 1
        Dim suzuki As Person
~
    alice.Name = "Alice"

          
~
  End Sub
        suzuki.Age = 20
-
        suzuki.Name = "鈴木"
-

          
-
    End Sub
-

          
 
End Module
End Module
 
}}
}}
 

        

        
~
このコードの概要を説明すると、まずIDとNameという二つのフィールドを持ったAccountクラスが宣言されています。 そして、このクラス型の変数``alice``を[[Mainプロシージャ>programming/vb.net/basics/00_console#EntryPoint]]で宣言しています。 さらに、宣言した変数``alice``に対して、IDフィールドとNameフィールドにそれぞれ値を割り当てています。
このコードの概要を説明すると、まずAgeとNameというフィールドを持ったPersonクラスが宣言されています。 そして、このクラス型の変数 suzukiをMain()プロシージャで宣言しています。 さらに、宣言した変数suzukiに対して、AgeとNameフィールドに値を割り当てています。 このコードは一見動作するように見えますが、実際にはAgeフィールドに値を設定しようとした時点で次のような例外エラーが発生します。
+

          
+
このコードは一見動作するように見えるかもしれませんが、実際にはIDフィールドに値を設定しようとした時点で次のような例外エラーが発生します。
 

        

        
 
#prompt(例外エラー){{
#prompt(例外エラー){{
 
ハンドルされていない例外 : System.NullReferenceException: オブジェクト参照がオブジェクト インスタンスに設定されていません。
ハンドルされていない例外 : System.NullReferenceException: オブジェクト参照がオブジェクト インスタンスに設定されていません。
~
   at Sample.Main() in E:\sample.vb:line 12
   at SampleConsoleApplication.MainModule.Main() in E:\Visual Basic .NET Program\SampleConsoleApplication\Module1.vb:line 14
 
}}
}}
 

        

        
~
このエラーメッセージは、変数``alice``にはインスタンスへの参照が設定されていないために、IDフィールドを設定する対象のインスタンスが無いことが原因で発生しています。 このように、具体的なインスタンスを格納していない場合クラス変数には初期値としてNothingが代入されているため、そのような変数を使ってインスタンスに対して操作を行おうとすると例外&msdn(netfx,type,System.NullReferenceException){NullReferenceException};がスローされます。
つまり、この変数にはインスタンスへの参照が設定されていない(この変数にNothingが代入されている)ために、Ageフィールドを参照・設定すべきインスタンスが見つからず、エラーになったわけです。 このように、クラスは必ずインスタンス化して使用しなければなりません。 次のコードは問題なく動作します。
+

          
+
クラスはインスタンス化することで使用できるようになります。 インスタンスの作成には``New``キーワードを使用します。 先ほどの例とは異なり、次のコードは問題なく動作します。
 

        

        
 
#code(vb){{
#code(vb){{
~
Imports System
Sub Main()
 

        

        
~
Class Account
    Dim suzuki As Person
+
  Public ID As Integer
+
  Public Name As String
+
End Class
 

        

        
~
Module Sample
    ' 新たにPersonクラスのインスタンスを作成し、suzukiへ代入
~
  Sub Main()
    suzuki = New Person()
+
    Dim alice As Account
+

          
+
    ' 新たにAccountクラスのインスタンスを作成し、変数aliceへ代入
+
    alice = New Account()
+

          
+
    ' 作成したインスタンスに対して操作を行う
+
    alice.ID = 1
+
    alice.Name = "Alice"
+
  End Sub
+
End Module
+
}}
+

          
+
次のように、変数の宣言およびインスタンスの作成と変数への代入を一行で記述することもできます。
 

        

        
~
#code(vb){{
    suzuki.Age = 20
~
' 変数aliceを宣言し、Accountクラスのインスタンスを作成して代入する
    suzuki.Name = "鈴木"
+
Dim alice As Account = New Account()
 

        

        
~
alice.ID = 1
End Sub
+
alice.Name = "Alice"
 
}}
}}
 

        

        
~
これをさらに簡略化した次のような構文もサポートされています。 どちらも同じ結果となりますが、一般的にはこの形式が多く使われているようです。
また、VB.NETでインスタンスを作成するには次のような形式も使用できます。
 

        

        
 
#code(vb){{
#code(vb){{
~
' 変数aliceを宣言し、Accountクラスのインスタンスを作成して代入する
' 変数の宣言とインスタンスの作成を同時に行う
~
Dim alice As New Account()
Dim suzuki As Person = New Person()
 

        

        
~
alice.ID = 1
suzuki.Age = 20
~
alice.Name = "Alice"
suzuki.Name = "鈴木"
 
}}
}}
 

        

        
~
インスタンスのフィールドに値が代入されているかを確認すると次のようになります。
VB.NETではこれをさらに省略した次の様な構文もサポートされています。
 

        

        
 
#code(vb){{
#code(vb){{
~
Imports System
' 変数の宣言とインスタンスの作成を同時に行う
~

          
Dim suzuki As New Person()
+
Class Account
+
  Public ID As Integer
+
  Public Name As String
+
End Class
+

          
+
Module Sample
+
  Sub Main()
+
    ' 変数aliceを宣言し、Accountクラスのインスタンスを作成して代入する
+
    Dim alice As New Account()
+

          
+
    alice.ID = 1
+
    alice.Name = "Alice"
+

          
+
    Console.WriteLine("ID = {0}, Name = {1}", alice.ID, alice.Name)
+
  End Sub
+
End Module
+
}}
 

        

        
~
#prompt(実行結果){{
suzuki.Age = 20
~
ID = 1, Name = Alice
suzuki.Name = "鈴木"
 
}}
}}
 

        

        
~

          
実際にインスタンスに値が代入されているかを確認してみます。
+
*構造体との共通点
+
クラスと構造体は機能的にはさまざまな点が共通しています。 メソッド、プロパティ、コンストラクタなどの要素は構造体の場合とほとんど変わりません。 クラスでは構造体と異なり引数を取らないコンストラクタを作成できるようになっています。
 

        

        
 
#code(vb){{
#code(vb){{
~
Imports System
Dim suzuki As Person = New Person()
 

        

        
~
Class Account
suzuki.Age = 20
~
  ' 内部的にIDを保持するためのフィールド
suzuki.Name = "鈴木"
+
  Private _ID As Integer
+
  ' 内部的に名前を保持するためのフィールド
+
  Private _Name As String
+

          
+
  ' Newという名前のメソッドはコンストラクタとして扱われる
+
  Public Sub New(ByVal id As Integer, ByVal name As String)
+
    _ID = id
+
    _Name = name
+
  End Sub
+

          
+
  ' クラスでは引数を取らないコンストラクタを作成することができる
+
  Public Sub New()
+
    _ID = 0
+
    _Name = "(default)"
+
  End Sub
+

          
+
  ' 次のような宣言により、クラスにプロパティを持たせることもできる
+
  Public Property ID() As Integer
+
    Get
+
      ' プロパティを介して返される値を返す
+
      Return _ID
+
    End Get
+
    Set(ByVal Value As Integer)
+
      ' プロパティを介して設定される値を保持する
+
      _ID = Value
+
    End Set
+
  End Property
+

          
+
  Public Property Name() As String
+
    Get
+
      Return _Name
+
    End Get
+
    Set(ByVal Value As String)
+
      _Name = Value
+
    End Set
+
  End Property
+

          
+
  ' メソッドを持たせることができるほか、基底クラスのメソッドを
+
  ' オーバーライドして動作を変えることもできる
+
  Public Overrides Function ToString() As String
+
    Return "ID = " + _ID.ToString() + ", Name = " + _Name
+
  End Function
+
End Class
 

        

        
~
Module Sample
Console.WriteLine("名前:{0} 年齢:{1}", suzuki.Name, suzuki.Age)
+
  Sub Main()
+
    ' コンストラクタにパラメータを指定しないでインスタンスを作成する
+
    Dim alice As New Account()
+

          
+
    ' プロパティの値を変更する
+
    alice.ID = 1
+
    alice.Name = "Alice"
+

          
+
    ' コンストラクタにパラメータを指定してインスタンスを作成する
+
    Dim bob As New Account(2, "Bob")
+

          
+
    ' コンストラクタにパラメータを指定してインスタンスを作成する
+
    Dim charlie As New Account(3, "Charlie")
+

          
+
    ' プロパティの値を変更する
+
    charlie.ID = 999
+

          
+
    ' ToStringメソッドを呼び出して文字列化して表示する
+
    Console.WriteLine(alice.ToString())
+
    Console.WriteLine(bob.ToString())
+

          
+
    ' Console.WriteLineは自動的にToStringを呼び出して文字列化することもできるので
+
    ' このようにしても同じ
+
    Console.WriteLine(charlie)
+
  End Sub
+
End Module
 
}}
}}
 

        

        
 
#prompt(実行結果){{
#prompt(実行結果){{
~
ID = 1, Name = Alice
名前:鈴木 年齢:20
+
ID = 2, Name = Bob
+
ID = 999, Name = Charlie
 
}}
}}
 

        

        
+
メソッド、プロパティ、コンストラクタの宣言方法については[[構造体での例>programming/vb.net/basics/09_structure]]や[[programming/vb.net/basics/08_module]]、[[programming/vb.net/basics/07_procedure]]を参照してください。 またフィールドやメソッドのスコープ(アクセシビリティ)については[[programming/vb.net/basics/11_accessibility]]で解説しています。
+

          
 
*参照型と値型
*参照型と値型
 
クラスと構造体の違いの一つに、値型であるか参照型であるかという違いがあります。 クラスは参照型で、構造体は値型です。
クラスと構造体の違いの一つに、値型であるか参照型であるかという違いがあります。 クラスは参照型で、構造体は値型です。
 

        

        
 
値型の変数は常にインスタンスを持ち、その値に対して直接アクセスするのに対して、参照型の変数ではインスタンスへの参照を通してインスタンスにアクセスします。 また、値型の変数に対して代入を行うとその値がコピーされるのに対して、参照型の変数に対して代入を行うと参照のみが代入され、実際のインスタンスはコピーされません。
値型の変数は常にインスタンスを持ち、その値に対して直接アクセスするのに対して、参照型の変数ではインスタンスへの参照を通してインスタンスにアクセスします。 また、値型の変数に対して代入を行うとその値がコピーされるのに対して、参照型の変数に対して代入を行うと参照のみが代入され、実際のインスタンスはコピーされません。
 

        

        
~
この違いを明確にするコードを使って解説します。
この違いを明確にするコードを次に記述します。
 

        

        
+
#column
 
#code(vb){{
#code(vb){{
~
Imports System
' 構造体(値型)
~

          
Structure ValPerson
+
' クラス(参照型)で実装した型
+
Class RefAccount
+
  Public Name As String
+
End Class
+

          
+
Module Sample
+
  Sub Main()
+
    ' クラス(参照型)のインスタンスを作成
+
    Dim a1 As New RefAccount
+
    Dim a2 As New RefAccount
+

          
+
    a1.Name = "Alice"
+
    a2.Name = "Bob"
+

          
+
    ' それぞれのNameフィールドの値を表示
+
    Console.WriteLine("{0} {1}", a1.Name, a2.Name)
+

          
+
    ' a1が参照しているインスタンスへの参照がa2に代入される
+
    a2 = a1
+

          
+
    ' a2.Nameの値を変更
+
    a2.Name = "Charlie"
+

          
+
    ' それぞれのNameフィールドの値を再度表示
+
    Console.WriteLine("{0} {1}", a1.Name, a2.Name)
+
  End Sub
+
End Module
+
}}
 

        

        
~
#prompt(実行結果){{
    Public Name As String
+
Alice Bob
+
Charlie Charlie
+
}}
+
#column
+
#code(vb){{
+
Imports System
 

        

        
+
' 構造体(値型)で実装した型
+
Structure ValAccount
+
  Public Name As String
 
End Structure
End Structure
 

        

        
~
Module Sample
Module MainModule
+
  Sub Main()
+
    ' 構造体(値型)のインスタンスを作成
+
    Dim a1 As New ValAccount
+
    Dim a2 As New ValAccount
+

          
+
    a1.Name = "Alice"
+
    a2.Name = "Bob"
+

          
+
    ' それぞれのNameフィールドの値を表示
+
    Console.WriteLine("{0} {1}", a1.Name, a2.Name)
+

          
+
    ' a1のコピーがa2に代入される
+
    a2 = a1
+

          
+
    ' a2.Nameの値を変更
+
    a2.Name = "Charlie"
+

          
+
    ' それぞれのNameフィールドの値を再度表示
+
    Console.WriteLine("{0} {1}", a1.Name, a2.Name)
+
  End Sub
+
End Module
+
}}
 

        

        
~
#prompt(実行結果){{
    Sub Main()
+
Alice Bob
+
Alice Charlie
+
}}
+
#column-end
 

        

        
~
この二つのコードにはクラスを使っているか構造体を使っているかの違いしかありませんが、実行結果の違い、参照型では代入後の``a2``に対する変更が``a1``にも影響している点に注目してください。
        ' 値型
-
        Dim valPerson1 As ValPerson
-
        Dim valPerson2 As ValPerson
 

        

        
~
構造体(値型)では、代入によって代入元変数``a1``のコピーが作成され代入先``a2``に設定されるため、代入先と代入元はどちらも違うインスタンスとなります。 一方クラス(参照型)では、代入によって代入元変数``a1``が参照しているインスタンスへの参照が代入先``a2``に設定されるため、代入先と代入元はどちらも同じインスタンスを参照することになります。 このため、クラスの場合では代入によって``a1``と``a2``には同じインスタンスへの参照が設定されるため、``a2``に対する変更は``a1``に対して変更することと同じとなり、実行結果にあるようにどちらも同じ値が表示されることになります。
        valPerson1.Name = "鈴木"
-
        valPerson2.Name = "佐藤"
 

        

        
~
このように、クラスと構造体はどちらも同じような機能を持っていますが、一方で値型と参照型という明確な動作の違いもあります。 どちらを選ぶかによってパフォーマンスが大きくことなることもあるほか、[[List(Of T)で構造体のフィールド・プロパティを変更する場合>programming/netfx/collections/2_generic_1_list#modify_valuetype_element]]のようにがわかりづらい問題が発生することもあるので、安易な判断を行わず両者の違いをよく理解して使い分ける必要があります。
        ' それぞれのNameフィールドの値を表示
-
        Console.WriteLine("{0} {1}", valPerson1.Name, valPerson2.Name)
 

        

        
~
このほか、値型・参照型の挙動の違いや注意点については[[programming/netfx/valuetype_referencetype]]、インスタンスのコピーについては[[programming/netfx/cloning]]で詳しく解説しています。
        ' valPerson1のコピーがvalPerson2に代入される
-
        valPerson2 = valPerson1
 

        

        
-
        ' valPerson1.Nameの値を変更
-
        valPerson1.Name = "山田"
 

        

        
~
*共有メンバ
        ' それぞれのNameフィールドの値を表示
~
クラスのフィールド・プロパティ・メソッドなどすべてのメンバはインスタンスを作成することで初めて使用できるようになります。 一方、``Shared``キーワードによってメンバを''共有メンバ''にすると、全インスタンスに共通したメンバ(各インスタンスではなく直接クラスに属するメンバ)となり、インスタンスを作成せず使用できるようになります。 クラス外から共有メンバを参照する場合は、&var{クラス名};.&var{メンバ名};のように記述します。
        Console.WriteLine("{0} {1}", valPerson1.Name, valPerson2.Name)
 

        

        
~
#code(vb){{
    End Sub
+
Imports System
+

          
+
Class Account
+
  ' 共有メンバ
+
  Private Shared Count As Integer = 0
+

          
+
  ' 非共有(インスタンス)メンバ
+
  Public ID As Integer
+
  Public Name As String
+

          
+
  Public Sub New()
+
    ' インスタンスが作成される度にCountをカウントアップする
+
    Count += 1
+
  End Sub
+

          
+
  ' 共有メソッド
+
  Public Shared Sub PrintCount()
+
    Console.WriteLine(Count)
+
  End Sub
+
End Class
 

        

        
+
Module Sample
+
  Sub Main()
+
    ' インスタンスを3つ作成する
+
    Dim alice As New Account()
+
    Dim bob As New Account()
+
    Dim charlie As New Account()
+

          
+
    ' インスタンスメンバの変更
+
    alice.ID = 1
+
    alice.Name = "Alice"
+

          
+
    ' 共有メソッドを呼び出して作成されたインスタンスの数を表示する
+
    Account.PrintCount()
+
  End Sub
 
End Module
End Module
 
}}
}}
 

        

        
 
#prompt(実行結果){{
#prompt(実行結果){{
~
3
鈴木 佐藤
-
山田 鈴木
 
}}
}}
 

        

        
~
[[アプリケーションのエントリポイント>programming/vb.net/basics/00_console#EntryPoint]]である``Main``メソッドをクラス内で宣言する場合も``Shared``で宣言します。 エントリポイントが呼び出される前にインスタンスを作成することはできないため、インスタンスを作成せずに``Main``メソッドを呼び出せるよう``Shared``を付ける必要があります。
このコードの流れを確認すると、まずvalPerson1とvalPerson2のNameフィールドにそれぞれ値を代入します。 この状態での実行結果は問題ないと思います。 つぎに、valPerson2にvalPerson1を代入します。 このとき、valPerson1の値のコピーが valPerson2に代入されます。 その後valPerson1.Nameの値を新しい値にし、その時の出力結果が二行目の出力結果です。 このように、値型の代入では変数のコピーが作成され代入されます。 続いて、これと似たもので参照型の場合を見てみましょう。
 

        

        
 
#code(vb){{
#code(vb){{
~
Imports System
' クラス(参照型)
-
Class RefPerson
 

        

        
~
Class Sample
    Public Name As String
+
  ' アプリケーションのエントリポイント
+
  Shared Sub Main()
+
    Console.WriteLine("Hello, world!")
+
  End Sub
+
End Class
+
}}
+

          
+
インスタンスを作成せずにメソッド呼び出しや変数の参照ができる[[モジュール>programming/vb.net/basics/08_module]]は、すべてのメンバが``Shared``(共有メンバ)となったクラスと見ることもできます。 一方、クラス自体にSharedキーワードを設定することはできないため、VBのクラスではC#の静的クラスに相当するものを作成することはできません。 VBではモジュールを使うことにより静的クラスに相当する機能を得ることができます。
+

          
+

          
+
*インスタンスの破棄 [#dispose]
+
``New``によって作成したインスタンスは''ガベージコレクタ''によって管理され、不要と判断された時点で自動的に破棄されるため、基本的にインスタンス自体を明示的に破棄する必要はなく、また解放処理を記述する必要もありません。
 

        

        
+
#code(vb){{
+
Imports System
+

          
+
Class Account
+
  Public Name As String
 
End Class
End Class
 

        

        
~
Module Sample
Module MainModule
+
  Sub Main()
+
    ' コンストラクタにパラメータを指定しないでインスタンスを作成する
+
    Dim alice As New Account()
+

          
+
    ' インスタンスを使った処理
+
    alice.Name = "Alice"
+
    '    :
+
    '    :
+
    '    :
 

        

        
~
    ' インスタンスが不要になっても明示的に破棄する必要はない
    Sub Main()
+
  End Sub
+
End Module
+
}}
 

        

        
~
しかし、インスタンスが''アンマネージリソース''などガベージコレクタによる管理の対象外となるリソースを作成・使用している場合は、それを確実に解放する必要があります。 ガベージコレクタの管理対象外となるリソースを扱うクラスを作成する場合は、&msdn(netfx,type,System.IDisposable){IDisposableインターフェイス};を実装して明示的にリソースを解放できる手段を用意するしておくことが推奨されます。 また、そのようなクラスのインスタンスを使用する際においては``Using``ステートメントを使うようにし、インスタンスを使用しおわった時点で確実に解放処理を呼び出させるようにします。
        ' 参照型
-
        Dim refPerson1 As New RefPerson()
-
        Dim refPerson2 As New RefPerson()
 

        

        
~
#code(vb){{
        refPerson1.Name = "鈴木"
~
Imports System
        refPerson2.Name = "佐藤"
 

        

        
~
' 明示的な解放が必要になるリソースを扱うクラス
        ' それぞれのNameフィールドの値を表示
~
Class Resource
        Console.WriteLine("{0} {1}", refPerson1.Name, refPerson2.Name)
+
  ' IDisposableインターフェイスを実装する
+
  Implements IDisposable
+

          
+
  Public Sub Dispose() Implements IDisposable.Dispose
+
    ' リソースの解放処理を記述する
+
  End Sub
+
End Class
+

          
+
Module Sample
+
  Sub Main()
+

          
+
    ' Usingステートメント内でインスタンスを作成・使用することにより、
+
    ' Disposeメソッドによる解放処理が確実に実行されるようにする
+
    Using res As New Resource()
+
      ' リソースを使った処理
+
    End Using
 

        

        
~
  End Sub
        ' refPerson1が参照しているインスタンスへの参照がrefPerson2に代入される
~
End Module
        refPerson2 = refPerson1
+
}}
 

        

        
~
次のように``Finalize``メソッドをオーバーライドすることによりデストラクタを記述することもできますが、このメソッドを明示的に呼び出すことはできません。 デストラクタはガベージコレクタによって呼び出されます。 ``IDisposable``を実装するクラスでは、確実に1度は``Dispose``メソッドが呼び出されるようにデストラクタを記述することはありますが、それ以外の多くの場合ではデストラクタを実装する必要はありません。
        ' refPerson1.Nameの値を変更
-
        refPerson1.Name = "山田"
 

        

        
~
#code(vb){{
        ' それぞれのNameフィールドの値を表示
~
Imports System
        Console.WriteLine("{0} {1}", refPerson1.Name, refPerson2.Name)
 

        

        
~
Class Resource
    End Sub
+
  Protected Overrides Sub Finalize()
+
    ' Finalizeメソッドをオーバーライドしてデストラクタを記述する場合は、
+
    ' 基底クラスのFinalizeメソッドも呼び出す必要がある
+
    MyBase.Finalize()
+
  End Sub
+
End Class
 

        

        
+
Module Sample
+
  Sub Main()
+
    Dim res As New Resource()
+

          
+
    ' このようにして直接デストラクタを呼び出すことはできない
+
    res.Finalize()
+
  End Sub
+
End Module
+
}}
+

          
+
[[インターフェイスの実装>#implement_interface]]については後述します。 IDisposableインターフェイスとリソースの解放、デストラクタの適切な実装については[[programming/netfx/disposing]]で詳しく解説しています。
+

          
+

          
+
*インスタンスの保存・復元
+
.NET Frameworkでは、[[シリアライズ>programming/netfx/serialization]]によってインスタンスの状態を保存・復元することができます。 シリアライズを行うことにより、インスタンスをバイナリ形式やXML形式でファイルに保存したり、ネットワークを経由して送受信したりすることができます。 シリアライズによるインスタンスの保存・復元では、クラスごとに保存・読み込み処理を記述する必要はなく、また[[属性>programming/netfx/attributes]]を付与することによって保存・復元時の動作を簡単にカスタマイズすることもできます。
+

          
+
シリアライズについては[[programming/netfx/serialization/0_abstract]]で詳しく解説しています。
+

          
+

          
+
*継承
+
VBではクラスの継承を行うことができます。 継承はクラスのみでサポートされる機能で、構造体は継承を行うことはできません。 また、VBでは多重継承を行うことはできません。
+

          
+
**クラスの継承
+
クラスを継承して派生クラスを作成するには、``Inherits``キーワードを使用します。 VBでは多重継承はサポートされないため、``Inherits``の後ろには常に基底クラスを一つだけ指定します。
+

          
+
#code(vb){{
+
Imports System
+

          
+
' 基底クラス
+
Class Base
+
  Sub Print()
+
    Console.WriteLine("Base")
+
  End Sub
+
End Class
+

          
+
' 派生クラス
+
Class Derived
+
  Inherits Base ' Baseクラスを継承
+
End Class
+

          
+
Module Sample
+
  Sub Main()
+
    Dim d As New Derived()
+

          
+
    d.Print()
+
  End Sub
 
End Module
End Module
 
}}
}}
 

        

        
 
#prompt(実行結果){{
#prompt(実行結果){{
~
Base
鈴木 佐藤
-
山田 山田
 
}}
}}
 

        

        
~
``Inherits``によって別のクラスを継承しているクラスを基底クラスとして更に継承を重ねることもできます。
こちらも一行目の出力結果までは問題ないと思います。 その後、refPerson2にrefPerson1を代入すると、refPerson2にはrefPerson1が参照しているインスタンスへの参照が代入されます。 この時点でrefPerson1とrefPerson2は同時に同じインスタンスを参照していることになります。 その次の行でrefPerson1.Nameの値を変更していますが、これは「refPerson1が参照しているインスタンスのNameフィールドの値を変更する」ことになります。
 

        

        
~
**オーバーライド
この状況でそれぞれのNameフィールドの値を表示してみると、いずれも同じインスタンスを参照しているので、それぞれ同じ値が表示されるわけです。 このように、値型と参照型では明確な動作の違いがあるので、良く理解しておく必要があります。 なお、値型・参照型の挙動の違いや注意点については[[programming/netfx/valuetype_referencetype]]、インスタンスのコピーについては[[programming/netfx/cloning]]で詳しく解説しています。
+
派生クラスで基底クラスのメソッドをオーバーライドする場合は、``Overrides``キーワードを使用します。 ただし、オーバーライドする場合はオーバーライドを許可することをあらわす``Overridable``キーワードが基底クラスのメソッドに付与されている必要があります。 ``Overridable``キーワードが付与されているメソッドは''仮想メソッド''となります。 ``Overrides``キーワードが付与されているメソッドは暗黙的に``Overridable``となるため、``Overrides Overridable``のようにこれらのキーワードを記述する必要はありません。
 

        

        
~
#code(vb){{
*構造体との共通点
~
Imports System
クラスと構造体では根本的な違いはあるものの、いくつかの点で共通する部分があります。 メソッド、プロパティ、コンストラクタなどの要素は構造体と変わりません。
+

          
+
' 基底クラス
+
Class Base
+
  ' オーバーライド可能なメソッド
+
  Overridable Sub Print()
+
    Console.WriteLine("Base")
+
  End Sub
+
End Class
+

          
+
' 派生クラス
+
Class Derived
+
  Inherits Base
+

          
+
  ' 基底クラスのPrintメソッドをオーバーライドして動作を変える
+
  Overrides Sub Print()
+
    Console.WriteLine("Derived")
+
  End Sub
+
End Class
+

          
+
Module Sample
+
  Sub Main()
+
    Dim d As New Derived()
+

          
+
    d.Print()
+
  End Sub
+
End Module
+
}}
+

          
+
#prompt(実行結果){{
+
Derived
+
}}
+

          
+
**抽象メソッド・抽象クラス
+
メソッドを抽象メソッドとする場合は、``Overridable``の代わりに``MustOverride``キーワードを指定します。 抽象メソッドを宣言する場合はメソッドの実装と``End Sub``の記述を省略します。 また``MustOverride``が付与されたメンバを含むクラスでは、そのクラスが抽象クラスであることを表す``MustInherit``キーワードをクラス宣言に付与する必要があります。 抽象クラスの継承と抽象メソッドのオーバーライドは通常のクラス・仮想メソッドの場合と同様です。
 

        

        
 
#code(vb){{
#code(vb){{
~
Imports System
Class Person
 

        

        
~
' 抽象クラス
    ' 内部的に名前を保持するためのフィールドです
~
MustInherit Class Base
    Private m_Name As String
+
  ' 抽象メソッド
+
  MustOverride Sub Print()
+
End Class
 

        

        
~
' 派生クラス
    ' 内部的に年齢を保持するためのフィールドです
~
Class Derived
    Private m_Age As Integer
+
  Inherits Base
+

          
+
  ' 抽象メソッドPrintの実装を記述する
+
  Overrides Sub Print()
+
    Console.WriteLine("Derived")
+
  End Sub
+
End Class
 

        

        
~
Module Sample
    ' クラスでは引数を取らないコンストラクタを指定することもできます
~
  Sub Main()
    Sub New()
+
    Dim d As New Derived()
 

        

        
~
    d.Print()
        m_Name = "(名無し)"
~
  End Sub
        m_Age = 0
+
End Module
+
}}
+

          
+
#prompt(実行結果){{
+
Derived
+
}}
+

          
+
ここまでの例では値を返さないメソッド(``Sub``プロシージャ)を使ってきましたが、値を返すメソッド(``Function``プロシージャ)やプロパティ(``Property``)の場合も同様にキーワードを指定することでメンバの仮想化・抽象化・オーバーライドを行うことができます。
 

        

        
~
また、``Overrides``などのキーワードとともに``Public``や``Protected``などのキーワードを組み合わせて指定することにより、メンバのアクセシビリティ(公開範囲)も設定することもできます。 これらのキーワードについては詳しくは[[programming/vb.net/basics/11_accessibility]]で解説しています。
    End Sub
+

          
+
**継承の禁止
+
クラスに``NotInheritable``キーワードを付与することにより、継承を禁止してシール(sealed, 封印)されたクラスを作成することができます。 ``NotInheritable``が付与されているクラスを継承しようとした場合はコンパイル時にエラーとなります。
+

          
+
#code(vb){{
+
Imports System
+

          
+
' 継承を許可しないクラス
+
NotInheritable Class Base
+
  Sub Print()
+
    Console.WriteLine("Base")
+
  End Sub
+
End Class
 

        

        
~
' 派生クラス
    ' 引数を取るコンストラクタは構造体と同じです
~
Class Derived
    Sub New(ByVal name As String, ByVal age As Integer)
+
  Inherits Base ' BC30299: 'Derived' をクラス 'Base' から継承できません。'Base' は 'NotInheritable' として宣言されています。
+
End Class
+
}}
 

        

        
~
**基底クラスのメンバ参照
        m_Name = name
~
派生クラス側から基底クラスを参照するには``MyBase``キーワードを使います。 例えば、派生クラスで基底クラスのコンストラクタを呼び出す場合は次の例のように``MyBase.New(&var{引数リスト...};)``とします。 コンストラクタで基底クラスのコンストラクタを呼び出す場合は、コンストラクタでの他の処理よりも先に呼び出すようにする必要があります。
        m_Age = age
 

        

        
~
#code(vb){{
    End Sub
+
Imports System
 

        

        
~
' 基底クラス
    ' プロパティの形式は構造体のものと同じです
~
Class Base
    Property Name() As String
~
  Private _name As String
        Get
~

          
            Return m_Name
~
  Sub New(ByVal name As String)
        End Get
+
    _name = name
+
  End Sub
+

          
+
  Sub Print()
+
    Console.WriteLine(_name)
+
  End Sub
+
End Class
 

        

        
~
' 派生クラス
        Set(ByVal Value As String)
~
Class Derived
            m_Name = Value
~
  Inherits Base
        End Set
~

          
    End Property
+
  Sub New(ByVal name As String)
+
    ' コンストラクタに渡された値を基底クラスのコンストラクタに引き渡す
+
    MyBase.New(name)
+
  End Sub
+
End Class
 

        

        
~
Module Sample
    Property Age() As Integer
~
  Sub Main()
        Get
~
    Dim d As New Derived("Derived")
            Return m_Age
-
        End Get
 

        

        
~
    d.Print()
        Set(ByVal Value As Integer)
~
  End Sub
            m_Age = Value
~
End Module
        End Set
~
}}
    End Property
 

        

        
~
#prompt(実行結果){{
    ' メソッドの形式も構造体と同じです
~
Derived
    Sub Introduce()
+
}}
 

        

        
~
コンストラクタだけでなく、メソッド・フィールド・プロパティなどの場合も同様に``MyBase``で基底クラスのメンバを参照することができます。 また、基底クラスを参照する``MyBase``以外にも、インスタンス自身を表す``Me``キーワード、自クラスのメンバを参照する``MyClass``キーワードなどを使ってメンバの参照を行うことができます。 ``Me``, ``MyClass``, ``MyBase``の3つのキーワードについては[[programming/vb.net/me_myclass_mybase]]で詳しく解説しています。
        Console.WriteLine("私は{0}, {1}歳です。", m_Name, m_Age)
 

        

        
~
**実行時の型判別
    End Sub
+
実行時にオブジェクトがあるクラスを継承しているか(型に互換性があるか)どうかを調べるには``TypeOf ... Is``を使用することができます。
 

        

        
+
#code(vb){{
+
Imports System
+

          
+
' 基底クラス
+
Class Base
+
End Class
+

          
+
' Baseを継承した派生クラス
+
Class Derived
+
  Inherits Base
+
End Class
+

          
+
' Baseとは無関係のクラス
+
Class Other
+
End Class
+

          
+
Module Sample
+
  Sub Main()
+
    Dim d As New Derived()
+
    Dim o As New Other()
+

          
+
    If TypeOf d Is Base Then
+
      Console.WriteLine("dはBaseクラスと互換性のある型です")
+
    Else
+
      Console.WriteLine("dはBaseクラスと互換性のない型です")
+
    End If
+

          
+
    If TypeOf o Is Base Then
+
      Console.WriteLine("oはBaseクラスと互換性のある型です")
+
    Else
+
      Console.WriteLine("oはBaseクラスと互換性のない型です")
+
    End If
+
  End Sub
+
End Module
+
}}
+

          
+
#prompt(実行結果){{
+
dはBaseクラスと互換性のある型です
+
oはBaseクラスと互換性のない型です
+
}}
+

          
+
TypeOf演算子・Is演算子については[[programming/vb.net/operator_is_isnot_typeof]]および[[programming/netfx/tips/check_type_isassignablefrom_issubclassof]]で詳しく解説しています。
+

          
+

          
+
*インターフェイスの実装 [#implement_interface]
+
VBではクラスに''インターフェイス''を実装することができます。 クラスの多重継承が許可されない代わりにインターフェイスは複数実装することができるため、インターフェイスを適切に実装することによってクラスに様々な機能を持たせることができます。
+

          
+
インターフェイスを実装するには、``Implements``キーワードを使用してクラスの先頭で実装するインターフェイス名を指定します。 次に、インターフェイスメソッドの''実装''を記述します。 この際も``Implements``キーワードを使用し、メソッド宣言の後ろに``Implements &var{インターフェイス名};.&var{メソッド名};``といった記述を付記します。 このような実装をインターフェイスが持つメソッドの分だけ記述します。
+

          
+
以下の例はクラスに&msdn(netfx,type,System.IDisposable){IDisposableインターフェイス};を実装する例です。 IDisposableインターフェイスは引数なし・戻り値無しのメソッドDisposeを持っているので、これをクラス内で実装するようにします。
+

          
+
#code(vb){{
+
Imports System
+

          
+
Class Resource
+
  ' IDisposableインターフェイスを実装する
+
  Implements IDisposable
+

          
+
  ' IDisposableインターフェイスのメソッドDisposeを実装する
+
  Sub Dispose() Implements IDisposable.Dispose
+
    ' メソッド内の実装は省略
+
  End Sub
 
End Class
End Class
+
}}
+

          
+
クラスは複数のインターフェイスを実装することができます。 次の例では、二つのインターフェイスI1とI2を実装するクラスを作成しています。 次の例のように、実装しようとするインターフェイスに同名のメソッドが含まれている場合は、実装となるメソッド名を変えることにより同名のインターフェイスメソッドを別々の名前で実装することができます。
+

          
+
#code(vb){{
+
Imports System
+

          
+
Interface I1
+
  Sub Method()
+
End Interface
+

          
+
Interface I2
+
  Sub Method()
+
End Interface
+

          
+
Class C
+
  ' 二つのインターフェイスI1, I2を実装する
+
  Implements I1, I2
+

          
+
  ' I1インターフェイスのメソッドMethodを、M1という名前のメソッドとして実装する
+
  Sub M1() Implements I1.Method
+
  End Sub
+

          
+
  ' I2インターフェイスのメソッドMethodを、M2という名前のメソッドとして実装する
+
  Sub M2() Implements I2.Method
+
  End Sub
+
End Class
+
}}
+

          
+
.NET Frameworkには様々な役割を持つインターフェイスが用意されています。 代表的なものとして、オブジェクトの破棄を行う機能を提供する''IDisposableインターフェイス''([[programming/netfx/disposing]])、コレクションに``For Each``による列挙機能を持たせる''IEnumerableインターフェイス''([[programming/netfx/enumeration]])、オブジェクト同士の大小関係を比較する機能を提供する''IComparableインターフェイス''([[programming/netfx/comparison/0_comparison]])、オブジェクトをシリアライズする際の動作を制御する''ISerializableインターフェイス''([[programming/netfx/serialization/1_formatter#ISerializable]])などがあります。 インターフェイスの具体的な実装例については、個々のドキュメントを参照してください。
 

        

        
 

        

        
~
*入れ子
Module MainModule
+
クラスでは別のクラスを入れ子にして宣言することができます。
+

          
+
#code(vb){{
+
Imports System
 

        

        
~
' 型のコンテナとなるクラス
    Sub Main()
+
Class C
+
  ' 入れ子になったクラス
+
  Class NC
+
  End Class
+

          
+
  Sub M()
+
    ' 入れ子になったクラスのインスタンスを作成・使用する
+
    Dim nc As New NC()
+
  End Sub
+
End Class
+
}}
 

        

        
~
入れ子にしたクラスには[[アクセシビリティ>programming/vb.net/basics/11_accessibility]]を設定できるため、クラス内でしか入れ子クラスを使用しない場合には入れ子クラスを``Private``にするといったこともできます。 このようにクラスは''型のコンテナ''として機能するため、クラスだけでなく[[構造体>programming/vb.net/basics/09_structure]]・[[列挙体>programming/netfx/enum]]・[[モジュール>programming/vb.net/basics/08_module]]・[[デリゲート>programming/netfx/delegate]]・インターフェイスなどもクラス内に宣言することができます。
        ' コンストラクタにパラメータを指定しないでインスタンスを作成します
-
        Dim tanaka As New Person()
 

        

        
-
        ' プロパティを通して値を指定します
-
        tanaka.Name = "田中"
-
        tanaka.Age = 23
 

        

        
~
*宣言の分割 (Partial)
        ' まずPerson型の変数だけ作成します
~
VB2005以降では、``Partial``キーワードを使用することによりクラスおよび構造体の宣言を分割することができます。 クラスのコードが長大になる場合や、自動生成部分と手動記述部分を分けて管理したい場合などには、``Partial``キーワードを使ってクラス宣言を複数箇所に分割して全体を把握しやすくすることができます。 ``Partial``キーワードを使ったクラスの宣言は、個別のファイルに記述することも、同一のファイルに記述することもできます。
        Dim saitou As Person
 

        

        
~
#code(vb,source1.vb){{
        ' コンストラクタにパラメータを指定してインスタンスを作成します
~
Imports System
        saitou = New Person("斉藤", 21)
 

        

        
~
' フィールド宣言のみを含むクラス宣言
        ' コンストラクタに何も指定しないでインスタンスを作成します
~
Partial Class Account
        Dim nanashi As New Person()
+
  Public ID As Integer
+
  Public Name As String
+
End Class
+
}}
 

        

        
+
#code(vb,source2.vb){{
+
Imports System
 

        

        
~
' コンストラクタとメソッドの宣言を含むクラス宣言
        ' 同じメソッドを呼び出すことでクラスの値を表示します
~
Partial Class Account
        tanaka.Introduce()
~
  Public Sub New(ByVal id As Integer, ByVal name As String)
        saitou.Introduce()
~
    Me.ID = id
        nanashi.Introduce()
+
    Me.Name = name
+
  End Sub
+

          
+
  Public Overrides Function ToString() As String
+
    Return "ID = " + ID.ToString() + ", Name = " + Name
+
  End Function
+
End Class
 

        

        
~
Module Sample
    End Sub
+
  Sub Main()
+
    Dim alice As New Account(1, "Alice")
 

        

        
+
    Console.WriteLine(alice)
+
  End Sub
 
End Module
End Module
 
}}
}}
 

        

        
 
#prompt(実行結果){{
#prompt(実行結果){{
~
ID = 1, Name = Alice
私は田中, 23歳です。
-
私は斉藤, 21歳です。
-
私は(名無し), 0歳です。
 
}}
}}
 

        

        
~

          
この例を見ていただければわかるとおり、プロパティ、メソッド、コンストラクタの要素は構造体のものと代わりありません。 ただ、コンストラクタについては引数を取らないコンストラクタを作成できることがクラスでの特徴になっています。 また、この例のようにコンストラクタもオーバーロードすることができます。
+
*クラスライブラリ
+
プロジェクトを実行可能形式(.exe)ではなくライブラリ(.dll)としてビルドすることにより、クラスライブラリを作成することができます。 これにより、作成したクラスを別のプロジェクトでも使用できるようにすることができます。 また、このようにして作成したクラスライブラリはプロジェクトの参照に追加するだけで使用できるようになり、またVBだけでなくC#など.NET Frameworkの他の言語からも使用することができるようになっています。
+

          
+
クラスライブラリの作成方法などについては[[programming/netfx/classlibrary/0_howtocreate]]で詳しく解説しています。
+

          
 

        

        
 
#navi(..)
#navi(..)
+

          
+