2003年12月19日
川俣晶の縁側技術関連執筆情報『VB6プログラマーのための入門Visual Basic.NET独習講座』読者サポート total 39594 count

継承を使用した場合のコンストラクタの使い方とMyBaseキーワード

Written By: 川俣 晶連絡先

 これは、書籍『VB6プログラマーのための入門Visual Basic.NET独習講座』の読者サポート情報として提供されている技術解説です。

コンストラクタと継承の関係 §

 本書285ページでMyBaseキーワードを紹介しているが、ここで紹介した他にMyBaseキーワードには重要な役割がある。それは、コンストラクタ内で、スーパークラスのコンストラクタを呼び出すという機能である。これは、普通のメソッド内で、スーパークラスのメソッド等を指定するために使うMyBaseキーワードとは意味合いが異なるものである。

 これを解説するために、以下のようなクラスを作成した。

Public Class ClassA

    Public Sub New()

        Trace.WriteLine("ClassA.New() called")

    End Sub

    Public Sub New(ByVal s As String)

        Trace.WriteLine("ClassA.New(ByVal s As String) called")

    End Sub

End Class

Public Class ClassB

    Inherits ClassA

    Public Sub New()

        Trace.WriteLine("ClassB.New() called")

    End Sub

End Class

Public Class ClassC

    Inherits ClassA

    Public Sub New()

        MyBase.New("sample")

        Trace.WriteLine("ClassC.New() called")

    End Sub

End Class

Public Class ClassD

    Public Sub New(ByVal s As String)

        Trace.WriteLine("ClassD.New(ByVal s As String) called")

    End Sub

End Class

'Public Class ClassE

'   Inherits ClassD

'   Public Sub New()

'       Trace.WriteLine("ClassE.New() called")  'Q:\aWrite\GH\vbn\AfterSamples\MyBaseInInherits\Form1.vb(34): 'MyBaseInInherits.ClassE' の基本クラス 'MyBaseInInherits.ClassD' には、引数なしで呼び出される、アクセス可能な 'Sub New' がないため、この 'Sub New' の最初のステートメントは、'MyBase.New' または 'MyClass.New' に対して呼び出さなければなりません。

'   End Sub

'End Class

 これを呼び出すために、以下のようなコードを作成した。

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        Dim b As New ClassB

        Dim c As New ClassC

    End Sub

 これを実行すると以下のようになる。

ClassA.New() called

ClassB.New() called

ClassA.New(ByVal s As String) called

ClassC.New() called

指定していないのに呼び出されるコンストラクタ §

 まず、実行結果の最初の2行に注目してみよう。

 この2行は、Dim b As New ClassBの結果として出力されたものである。ここでは、ClassBのインスタンスしか作成していないにも関わらず、ClassAとClassBの両方のコンストラクタが呼び出されている。これは。ClassBがClassAを継承しているためである。基本的に、他のクラスを継承したクラスのインスタンスを作成する場合、そのスーパークラスのコンストラクタも呼び出される。継承が何段階も行われていれば、全ての段階のコンストラクタが呼び出される。呼び出される順番は、常にスーパークラスが先である。

コンストラクタから明示的に指定したスーパークラスのコンストラクタを呼び出す §

 次に実行結果の残りの2行に注目してみよう。

 この2行は、Dim c As New ClassCの結果として出力されたものである。ClassCは、ClassBと同じようにClassAを継承しているにも関わらず、前の例と異なり、ClassAのコンストラクタの引数のあるコンストラクタが呼び出されている。呼び出されるコンストラクタが違っている理由は、MyBase.New("sample")という行の存在による。これは、スーパークラスのコンストラクタを明示的に呼び出すことを指定するコードである。これに文字列型の引数が記述されているため、スーパークラスのコンストラクタの中で、文字列型の引数を持つものが呼び出されたわけである。世の中には、引数を持つコンストラクタも多いが、それを活用するために知っておいて損はない書き方である。

 しかし、MyBase.New(……)はコンストラクタ内のどこにでも書けるわけではない。これは、あくまでメソッドの先頭、一般のステートメントよりも手前に記述しなければならない。このような制約が付いている点で、普通のメソッド内で、スーパークラスのメソッド等を指定するために使うMyBaseキーワードとは意味合いが異なるものである。

引数のないコンストラクタがない場合 §

 最後に、ClassDとコメントアウトしたClassEを見て頂きたい。このClassEのコメントを取ると、'MyBaseInInherits.ClassE' の基本クラス 'MyBaseInInherits.ClassD' には、引数なしで呼び出される、アクセス可能な 'Sub New' がないため、この 'Sub New' の最初のステートメントは、'MyBase.New' または 'MyClass.New' に対して呼び出さなければなりません。というコンパイルエラーとなる。

 スーパークラスのコンストラクタを明示的に呼び出さない場合、スーパークラスの引数の無いコンストラクタが選ばれ、呼び出される。しかし、ClassDにはそのようなコンストラクタが存在しない。そのため、ClassEは呼び出すべきコンストラクタを見いだせず、コンパイルエラーとなる。

 なお、コンストラクタが1つも存在しないクラスを継承するときには、このようなエラーが発生することはない。それは、コンストラクタが1つも存在しないクラスには、引数のないコンストラクタが存在すると見なされるためである。これを、既定のコンストラクタという。しかし、1つでもコンストラクタを記述すれば、既定のコンストラクタがあるとは見なされない。

 既定のコンストラクタのアクセスの種類は、Publicとなっている。