Visual Basic のオブジェクトとクラス

"オブジェクト" は、1 つの単位として扱うことができるコードとデータの組み合わせです。 オブジェクトは、コントロールやフォームのように、アプリケーションの一部になることができます。 アプリケーション全体も、オブジェクトになることができます。

Visual Basic でアプリケーションを作成するときは、常にオブジェクトを操作します。 Visual Basic に用意されている、コントロール、フォーム、データ アクセスなどのオブジェクトを使用できます。 他のアプリケーションのオブジェクトを、作成中の Visual Basic アプリケーションの中で使用することもできます。 独自のオブジェクトを作成し、それらのプロパティとメソッドを追加で定義することもできます。 オブジェクトはプログラムの作成済みの構成要素として機能し、コードを一度記述すれば、何度も再利用できます。

このトピックでは、オブジェクトの詳細について説明します。

オブジェクトとクラス

Visual Basic 内の各オブジェクトは、"クラス" によって定義されます。 クラスは、オブジェクトの変数、プロパティ、プロシージャ、およびイベントを記述します。 オブジェクトはクラスのインスタンスです。クラスを定義したら、必要な数のオブジェクトを作成することができます。

オブジェクトとそのクラス間の関係を理解するために、クッキーの抜き型とクッキーを考えてみましょう。 クッキーの抜き型はクラスです。 それは、クッキーの特徴 (大きさや形など) を定義します。 クラスを使用して、オブジェクトを作成します。 オブジェクトはクッキーです。

メンバーにアクセスするには、そのオブジェクトを作成する必要があります。ただし、クラスのオブジェクトを使用せずにアクセスできる Shared のメンバーは除きます。

クラスからオブジェクトを作成する

  1. オブジェクトの作成元となるクラスを決定するか、独自のクラスを定義します。 次に例を示します。

    Public Class Customer
        Public Property AccountNumber As Integer
    End Class
    
  2. Dim ステートメントを記述して、クラス インスタンスを割り当てることができる変数を作成します。 変数は、目的のクラスの型にする必要があります。

    Dim nextCustomer As Customer
    
  3. New 演算子キーワードを追加して、変数をクラスの新しいインスタンスに初期化します。

    Dim nextCustomer As New Customer
    
  4. これで、オブジェクト変数を使用してクラスのメンバーにアクセスできるようになりました。

    nextCustomer.AccountNumber = lastAccountNumber + 1
    

Note

可能であれば、変数は、変数に割り当てる予定のクラスの型で宣言する必要があります。 これは、事前バインディングと呼ばれます。 コンパイル時にクラスの型を把握していない場合は、変数を Object データ型で宣言することで、遅延バインディングを呼び出すことができます。 ただし、遅延バインディングでは、パフォーマンスが低下し、実行時のオブジェクトのメンバーへのアクセスが制限される可能性があります。 詳細については、「オブジェクト変数の宣言」を参照してください。

複数インスタンス

多くの場合、クラスから新しく作成されたオブジェクトは互いに同一です。 ただし、個々のオブジェクトとして存在した後、それぞれの変数とプロパティは、他のインスタンスとは無関係に変更することができます。 たとえば、次の 3 つのチェック ボックスをフォームに追加する場合、各チェック ボックスのオブジェクトは、CheckBox クラスのインスタンスになります。 個々の CheckBox オブジェクトは、クラスによって定義された特性と機能 (プロパティ、変数、プロシージャ、およびイベント) の共通セットを共有します。 ただし、それぞれが独自の名前を持ち、別々に有効または無効にすることができ、フォームの異なる場所に配置することができます。

オブジェクトのメンバー

オブジェクトはアプリケーションの要素であり、クラスの "インスタンス" を表しています。 フィールド、プロパティ、メソッド、およびイベントは、オブジェクトの構成要素であり、オブジェクトのメンバーを構成します。

メンバー アクセス

オブジェクトのメンバーにアクセスするには、オブジェクト変数の名前、ピリオド (.)、およびメンバーの名前をこの順序で指定します。 Label オブジェクトの Text プロパティを設定する例を次に示します。

warningLabel.Text = "Data not saved"

メンバーの IntelliSense の一覧

IntelliSense は、[メンバーの一覧] オプションが呼び出されたとき (たとえばメンバー アクセス演算子としてピリオド (.) が入力されたとき) に、クラスのメンバーを一覧表示します。 ピリオドに続けて、そのクラスのインスタンスとして宣言された変数の名前を入力すると、IntelliSense は、すべてのインスタンス メンバーを表示しますが、共有メンバーは表示しません。 クラス名に続けてピリオドを入力すると、IntelliSense は、すべての共有メンバーを表示し、インスタンス メンバーは表示しません。 詳細については、「IntelliSense の使用」を参照してください。

フィールドとプロパティ

"フィールド" と "プロパティ" は、オブジェクトに格納されている情報を表します。 代入ステートメントによる値の取得と設定は、プロシージャでローカル変数の取得と設定を行うのと同じ方法で実行します。 次の例では Width プロパティを取得し、Label オブジェクトの ForeColor プロパティを設定しています。

Dim warningWidth As Integer = warningLabel.Width
warningLabel.ForeColor = System.Drawing.Color.Red

フィールドは、"メンバー変数" とも呼ばれます。

次の場合は、プロパティ プロシージャを使用します。

  • 値を設定または取得するタイミングと方法を制御する必要がある。

  • プロパティに、検証する必要がある適切に定義された一連の値がある。

  • 値を設定することで、オブジェクトの状態をはっきりと変更する (例: IsVisible プロパティ)。

  • プロパティを設定することで、他の内部変数またはその他のプロパティの値を変更する。

  • プロパティを設定または取得する前に、一連の手順を実行する必要がある。

次の場合は、フィールドを使用します。

  • 値は自己検証型である。 たとえば、Boolean 変数に True または False の値が割り当てられた場合は、エラーまたはデータの自動変換が発生します。

  • データ型によってサポートされている範囲の値はすべて有効である。 これは、Single 型または Double 型の多くのプロパティに当てはまります。

  • プロパティは String データ型であり、文字列のサイズまたは値に制限がない。

  • 詳細については、「プロパティ プロシージャ」を参照してください。

ヒント

定数でないフィールドは常に非公開にしておいてください。 公開する場合は、代わりにプロパティを使用します。

メソッド

"メソッド" は、オブジェクトが実行できる処理です。 たとえば、Add は、コンボ ボックスに新しいエントリを追加する ComboBox オブジェクトのメソッドです。

次の例は、Timer オブジェクトの Start メソッドを示しています。

Dim safetyTimer As New System.Windows.Forms.Timer
safetyTimer.Start()

メソッドは、オブジェクトによって公開される "プロシージャ" です。

詳細については、「プロシージャ」を参照してください。

イベント

イベントは、マウスのクリックやキーの押下などのオブジェクトによって認識される操作であり、それに応答するコードを記述できます。 イベントは、ユーザーの操作またはプログラム コードの結果として発生する可能性がありますが、システムによって発生する場合もあります。 イベントを通知するコードを、イベントを "発生させる" と言い、イベントに応答するコードを、イベントを "処理する" と言います。

オブジェクトによって発生する独自のカスタム イベントを作成し、その他のオブジェクトで処理することもできます。 詳細については、「イベント」を参照してください。

インスタンス メンバーと共有メンバー

クラスからオブジェクトを作成した結果が、そのクラスのインスタンスになります。 Shared キーワードなしで宣言されたメンバーは、"インスタンス メンバー" になり、特定のインスタンスにのみ属します。 あるインスタンスのインスタンス メンバーは、同じクラスの別のインスタンスに同じメンバーがあっても互いに独立しています。 たとえばインスタンス メンバー変数は、インスタンスごとに異なる値を持つことができます。

Shared キーワード付きで宣言されたメンバーは "共有メンバー" になり、全体がクラスに属し、特定のインスタンスには属しません。 共有メンバーは、作成するクラスのインスタンスの数に関係なく 1 度だけ存在します。これは、インスタンスを作成していない場合でも同じです。 たとえば共有メンバー変数は、クラスにアクセスできるすべてのコードが使用できる 1 つの値のみを持っています。

非共有メンバーへのアクセス

  1. オブジェクトがクラスから作成され、オブジェクト変数に割り当てられていることを確認してください。

    Dim secondForm As New System.Windows.Forms.Form
    
  2. メンバーにアクセスするステートメントで、オブジェクト変数名に続けて、"メンバー アクセス演算子" (.) とメンバー名を指定します。

    secondForm.Show()
    

共有メンバーへのアクセス

  • クラス名に続けて、"メンバー アクセス演算子" (.) とメンバー名を指定します。 オブジェクトの Shared メンバーは、常にクラス名から直接アクセスする必要があります。

    Console.WriteLine("This computer is called " & Environment.MachineName)
    
  • 既にクラスからオブジェクトを作成している場合は、オブジェクト変数を使用して Shared メンバーにアクセスすることもできます。

クラスとモジュールの違い

クラスとモジュールの主な違いは、クラスはオブジェクトとしてインスタンス化できますが、標準モジュールではできないことです。 標準モジュールのデータは 1 つしかないため、プログラムのある部分が標準モジュールのパブリック変数を変更した場合、その後、プログラムの他の部分がその変数を読み取ると、変更後の値が取得されます。 これに対し、オブジェクト データは、インスタンス化されたオブジェクトごとに個別に存在します。 別の相違点は、標準モジュールとは異なり、クラスはインターフェイスを実装できることです。 クラスが MustInherit 修飾子でマークされている場合、それを直接インスタンス化することはできません。 ただし、これはモジュールとは異なります。モジュールとは異なり、継承できるためです。

Note

Shared 修飾子は、クラス メンバーに適用された場合は、クラスの特定のインスタンスではなく、クラス自体に関連付けられます。 メンバーへのアクセスは、モジュール メンバーへのアクセス方法と同じように、クラス名を使用して直接行います。

さらに、クラスとモジュールは、メンバーに対して異なるスコープを使用します。 クラス内に定義されているメンバーのスコープはクラスの特定のインスタンス内に限られ、オブジェクトの有効期間のみ存在します。 クラスの外部からクラス メンバーにアクセスするには、"オブジェクト.メンバー" 形式の完全修飾名を使用する必要があります。

その一方で、モジュール内に宣言されているメンバーは、既定ではパブリックにアクセスすることができ、モジュールにアクセスできるすべてのコードからアクセスできます。 つまり、標準モジュール内の変数は、プロジェクト内の任意の場所から参照でき、プログラムが実行されている間は存在するため、実質的にはグローバル変数です。

クラスとオブジェクトの再利用

オブジェクトを使用すると、変数とプロシージャを 1 度宣言した後、必要に応じていつでもそれらを再利用できます。 たとえば、スペル チェック機能をアプリケーションに追加する場合は、スペル チェック機能を提供するためのすべての変数とサポート機能を定義することができます。 スペル チェック機能をクラスとして作成した場合は、コンパイルされたアセンブリへの参照を追加することで、他のアプリケーションで再利用できます。 さらに、誰かが既に開発したスペル チェック機能のクラスを使用することで、作業の手間を省くことができます。

.NET には、使用可能なコンポーネントの例が多数用意されています。 次の例では、System 名前空間に TimeZone クラスが使用されています。 TimeZone は、現在のコンピューター システムのタイム ゾーンに関する情報を取得できるメンバーを提供します。

Public Sub ExamineTimeZone()
    Dim tz As System.TimeZone = System.TimeZone.CurrentTimeZone
    Dim s As String = "Current time zone is "
    s &= CStr(tz.GetUtcOffset(Now).Hours) & " hours and "
    s &= CStr(tz.GetUtcOffset(Now).Minutes) & " minutes "
    s &= "different from UTC (coordinated universal time)"
    s &= vbCrLf & "and is currently "
    If tz.IsDaylightSavingTime(Now) = False Then s &= "not "
    s &= "on ""summer time""."
    Console.WriteLine(s)
End Sub

上記の例では、最初の Dim ステートメントTimeZone 型のオブジェクト変数を宣言し、CurrentTimeZone プロパティによって返された TimeZone オブジェクトに割り当てています。

オブジェクト間の関係

オブジェクトは、いくつかの方法で相互に関連付けることができます。 リレーションシップの主要な種類は、"階層" と "含有" です。

階層関係

より基礎的なクラスから派生したクラスは、"階層関係" があると言います。 クラスの階層は、より一般的なクラスのサブタイプである項目を記述する場合に便利です。

次の例では、通常の Button と同じように機能しますが、前景と背景の色を逆転させるメソッドも公開する、特殊な Button を定義します。

既に存在するクラスから派生するクラスを定義する

  1. Class ステートメントを使用して、必要なオブジェクトの作成元となるクラスを定義します。

    Public Class ReversibleButton
    

    クラスのコードの最後の行の後に、必ず End Class ステートメントを指定してください。 統合開発環境 (IDE) では、既定により、Class ステートメントを入力すると、End Class が自動的に生成されます。

  2. Class ステートメントの直後に、Inherits ステートメントを記述します。 新しいクラスの派生元クラスを指定します。

    Inherits System.Windows.Forms.Button
    

    新しいクラスは、基本クラスによって定義されたすべてのメンバーを継承します。

  3. 派生クラスで公開される追加のメンバー用のコードを追加します。 たとえば、ReverseColors メソッドを追加すると、派生クラスは次のようになります。

    Public Class ReversibleButton
        Inherits System.Windows.Forms.Button
            Public Sub ReverseColors()
                Dim saveColor As System.Drawing.Color = Me.BackColor
                Me.BackColor = Me.ForeColor
                Me.ForeColor = saveColor
           End Sub
    End Class
    

    ReversibleButton クラスからオブジェクトを作成すると、オブジェクトは、Button クラスのすべてのメンバーだけでなく、ReverseColors メソッドと ReversibleButton に定義したその他の新しいメンバーにもアクセスできます。

派生クラスは、派生元のクラスからメンバーを継承するため、クラス階層が深くなるにつれて、クラスをより複雑にすることができます。 詳細については、「継承の基本」を参照してください。

コードのコンパイル

コンパイラが、新しいクラスの派生元となるクラスにアクセスできることを確認します。 これは、上記の例のように名前を完全に修飾するか、名前空間を Imports ステートメント (.NET 名前空間と型) で識別することを意味する場合があります。 クラスが別のプロジェクト内にある場合は、そのプロジェクトへの参照の追加が必要になることがあります。 詳細については、「プロジェクト内の参照の管理」を参照してください。

含有リレーションシップ

オブジェクトを関連付けることのできる別の方法は、"含有関係" にすることです。 コンテナー オブジェクトは、その他のオブジェクトを論理的にカプセル化します。 たとえば、OperatingSystem オブジェクトには、その Version プロパティによって返される Version オブジェクトが論理的に含まれています。 コンテナー オブジェクトが物理的にその他のオブジェクトを含んでいるわけではないことに注意してください。

コレクション

特別な種類のオブジェクトの含有として、"コレクション" と表現されるものがあります。 コレクションは、列挙することができる類似のオブジェクトの集まりです。 Visual Basic では、For Each...Next ステートメント で特別な構文をサポートしています。これを使用して、コレクションの項目を反復処理することができます。 さらに、多くの場合、コレクションでは、Item[] を使用して、その要素を、インデックスまたは一意の文字列の関連付けによって取得することができます。 コレクションは、インデックスなしで項目を削除または追加できるため、配列よりも簡単に使用できます。 簡単に使用できるため、多くの場合、コレクションは、フォームとコントロールを格納するために使用されます。

チュートリアル: クラスの定義
クラスを作成する方法を手順を追って説明します。

オーバーロードされたプロパティとメソッド
オーバーロードされたプロパティとメソッド

継承の基本
継承修飾子、メソッドとプロパティのオーバーライド、MyClass、および MyBase について説明します。

オブジェクトの有効期間:オブジェクトの作成と破棄
クラス インスタンスの作成と破棄について説明します。

匿名型
匿名型を作成して使用する方法について説明します。匿名型を使用すると、データ型のクラス定義を記述せずにオブジェクトを作成できます。

オブジェクト初期化子: 名前付きの型と匿名型
1 つの式で名前付きの型と匿名型のインスタンスを作成するために使用する、オブジェクト初期化子について説明します。

方法: 匿名型の宣言におけるプロパティ名と型を推論する
匿名型で宣言されたプロパティの名前と型を推論する方法について説明します。 推論の成功例と失敗例を示します。