Visual Basic 中級講座
VB.NET 2002 対応 VB.NET 2003 対応 VB2005 対応 VB2008 対応

 

Visual Basic 中学校 > 中級講座 >

第3回 継承の制御

 

継承にまつわる各種機能および、継承を強制したり継承を不能にする制御について説明します。

概要

・派生クラスではMyBaseを使用することで基底クラスにアクセスできる。

・Meは常に現在実行中のインスタンスを指す。慣れないうちはMeやMyClassが何を指しているか注意する必要がある。

・派生クラスでのコンストラクタの定義には少し特殊な仕様がある。

・基底クラスのメソッドやプロパティと名前は同じだが無関係なメソッドやプロパティを作成するにはShadowsを使用する。

・MustInherit・MustOverrideは継承を強制し、そのクラスのインスタンスを作成できなくする。→抽象クラス。

・NotInheritable・NotOverridableは継承を不可能にする。

 

1.代名詞

 

前回、継承の基本的なキーワードの使用方法を説明しました。

今回はまず基本に戻ってクラス自身を表すキーワードMeについて考えてみます。また、Meとよく似ているMyClassMyBaseについても説明します。

ここで説明する内容を正しく理解していないと、あとあとポリモーフィズムの一環として継承を使用することが困難になるかもしれませんし、何よりも意図したとおりにMeが動作してくれないと言うことにもなりかねません。

まず、継承とは直接関係ありませんが「Me」は正確にいえばクラス自身ではなく現在のインスタンスを指しているキーワードです。ですから共有メンバの内部ではMeを使用することはできません。共有メンバでは使用できないと言う性質はMyBaseとこれから説明するMyClassにも当てはまりますし、キーワードの意味から考えると当然のことです。

以下では「Me」の性質にてついていろいろと説明していますが、Meが省略可能である点にも注意してください。Meが省略されている部分にも以下で説明するMeの性質は当てはまります。

 

次の小さなManmalクラスとApeクラスを例に説明します。

VB.NET2002対応 VB.NET2003対応 VB2005対応 VB2008対応

Public Class Manmal

    Public
Overridable ReadOnly Property JanapeseName() As
String
        Get
           
Return "哺乳類"
       
End Get
   
End Property
    Public Overridable Sub Say()
        MsgBox(Me.JanapeseName)
    End
Sub

End
Class
Public Class Ape
    Inherits Manmal

End Class

■リスト1

Manmalクラスのインスタンスを作成してSayメソッドを呼び出すと「哺乳類」と表示されます。これは問題ないでしょう。

ではApeクラスのインスタンスを作成してSayメソッドを呼び出すとどうなるでしょうか?

ポイントはSayメソッドがMe.JapaneseNameを表示するようになっていることです。ApeクラスではJapaneseNameプロパティもSayメソッドもオーバーロードしていません。この場合、Me.JapaneseNameはどう解釈されるでしょうか?

VB.NET2002対応 VB.NET2003対応 VB2005対応 VB2008対応

Dim a As New Ape
a.Say()

■リスト2

単純な話ではありますが、実際に試してみてください。答えは「哺乳類」です。

■画像1

 

では、Apeクラスでの実装を次のように変更したときのことを考えます。

VB.NET2002対応 VB.NET2003対応 VB2005対応 VB2008対応

Public Class Ape

    Inherits
Manmal

    Public Overrides ReadOnly Property JanapeseName() As
String
        Get
           
Return "類人猿"
       
End Get
   
End Property

End
Class

■リスト3

この例は非常に注意を要します。SayメソッドはManmalクラス内で実装されているので、Me.JapaneseNameの部分の「Me」とはManmalクラスを指すように思ってしまうかもしれませんが、この場合はApeクラスのインスタンスを指すことになります。

ですから、これでApeクラスのSayメソッドを呼び出すと「類人猿」と表示されます。

これは「Me」が現在のインスタンスを指しているためと考えると理解しやすいです。Sayメソッドは確かにManmalクラス内で実装されていますが、そのコードを実行しているインスタンスはまぎれもなくApeクラスのインスタンスです。ですからMeApeクラスの方を指すことになると考えるわけです。

この「Me」の性質は単に注意が必要なだけではなく、大きな可能性を秘めておりオブジェクト指向を活用する上で非常に重要なものになります。詳しくは今後のポリモーフィズムの説明の中で取り上げていく予定です。

■画像2

 

 

キーワードMyClassは常に現在のメンバが実装されているクラスを表します。今度はManmalクラスのMeの部分をMyClassに置き換えて実行してみてください。コードの全体は次の通りです。

VB.NET2002対応 VB.NET2003対応 VB2005対応 VB2008対応

Public Class Manmal

    Public Overridable ReadOnly Property JanapeseName() As
String
        Get
           
Return "哺乳類"
       
End Get
   
End Property
    Public Overridable Sub Say()
        MsgBox(MyClass.JanapeseName)
    End
Sub

End
Class
Public Class Ape

    Inherits
Manmal

    Public Overridable ReadOnly Property JanapeseName() As
String
        Get
           
Return "類人猿"
       
End Get
   
End Property

End
Class

■リスト4

この場合SayメソッドがManmalクラスで実装されているので、MyClass.JapaneseNameは常にManmalクラス内で実装されているJapaneseNameプロパティを指すことになります。ですからApeクラスのSayメソッドを呼び出しても「哺乳類」と表示されます。

MeMyClassはなれない方にはかなり紛らわしいものと映るでしょう。参考のために言っておくとMyClassを使用することはあまりありません。 どちらを使用すべきか悩んだらMeの方が適切である場合が圧倒的に多いです。

■画像3

 

MyBaseは常に基底クラスを指します。こちらはすでに登場していますし、使い方に悩むことはほとんどないでしょう。

 

2.シャドウ

 

さて、基底クラスの機能を変更してしまうオーバーライドは簡単に利用できる上に非常に便利な機能ですが、元となっているメンバの名前や引数の型・順序、戻り値の型、それに適用範囲を変更することはできません。

もし、基底クラスのメンバと名前は同じにしたいけれども引数や戻り値や適用範囲を変更したいと言う場合にはシャドウイングを行います。

もう一度先程のManmalクラスを利用して説明します。

VB.NET2002対応 VB.NET2003対応 VB2005対応 VB2008対応

Public Class Manmal

    Public Overridable ReadOnly Property JanapeseName() As
String
        Get
           
Return "哺乳類"
       
End Get
   
End Property
    Public Overridable Sub Say()
        MsgBox(MyClass.JanapeseName)
    End
Sub

End
Class

■リスト5

このManmalクラスを継承してApeクラスを作成するときに、JapaneseNameメソッドに引数を追加してオーバーライドすることはできません。

引数を追加するとどのようなことになるか、実際に試してみます。

VB.NET2002対応 VB.NET2003対応 VB2005対応 VB2008対応

Public Class Ape

    Inherits
Manmal

    Public ReadOnly Property JanapeseName(ByVal IsHiragana As Boolean) As
String
        Get

           
If IsHiragana Then
               
Return "るいじんえん"
           
Else
               
Return "類人猿"
           
End If

       
End Get
   
End Property

End
Class

■リスト6

まず、Overridesキーワードを付けることはできなくなります。つけるとビルドエラーになります。Overridesキーワードを取り除くとビルドして実行できるようになります。ただし、Overridesキーワードを取り除いた場合基底クラスのJapaneseNameプロパティと派生クラスのJapaneseNameプロパティはたまたま名前が同じなだけの別のプロパティとして認識されます。この構造を「シャドウ」または「シャドウイング」と呼びます。「派生クラスのJapaneseNameプロパティは基底クラスのプロパティをシャドウしている」などと表現します。

この状態でApeクラスのSayメソッドを呼び出してみましょう。

VB.NET2002対応 VB.NET2003対応 VB2005対応 VB2008対応

Dim a As New Ape
a.Say()

■リスト7

「哺乳類」と表示されます。ApeクラスのJapaneseNameプロパティはまるっきり無視です。

前に紹介した例では、ApeクラスのSayメソッドはキーワードMeを使用しているので、派生クラスでオーバーライドされているプロパティが呼び出されると言う趣旨のことを書きました。それはどの通りなのですが今回の例ではJapaneseNameプロパティは名前は同じですがオーバーライドされていないので呼び出されなくなります。

■画像4

この現象は話が無駄に複雑になるので、できるだけ基底クラスのメンバをシャドウするようなことは止めましょう。無理に同じ名前のメソッドやプロパティを作成しないで名前を変えるかせめてオーバーロードしましょう(オーバーライドとオーバーロードは名前が似ていますが別物です)。

さらに、それでもどうしても同じ名前のメソッドやプロパティを作りたくなったら明確に区別するためにキーワードShadowsをつけるようにしましょう。

キーワードShadowsには特別な機能はありませんが、ソースコードを見たときに基底クラスのメンバをシャドウしていることがはっきりして少しだけわかりやすくなります。

VB.NET2002対応 VB.NET2003対応 VB2005対応 VB2008対応

Public Class Ape

    Inherits
Manmal

    Public Shadows ReadOnly Property JanapeseName(ByVal IsHiragana As Boolean) As
String
        Get

           
If IsHiragana Then
               
Return "るいじんえん"
           
Else
               
Return "類人猿"
           
End If

       
End Get
   
End Property

End
Class

■リスト8

 

ところで、Overridesと間違ってOverridableと書いてしまった場合もシャドウイングが行われます。メンバの名前のところに緑の波線がでて警告が表示されるので気がつくとは思いますがご注意ください。

要するにOverrides抜きで基底クラスのメンバと同じ名前のメンバを定義するとすべてシャドウになるのです。

 

ここで説明したのは「継承によるシャドウ」です。この他に適用範囲によってもシャドウが行われる場合があります。話題がそれますが少しだけ紹介しておきます。

VB.NET2002対応 VB.NET2003対応 VB2005対応 VB2008対応

Dim Age As Integer

Public
Sub New(ByVal Age As Integer)
    Me.Age = Age
End Sub

■リスト9

このコードではコンストラクタの内部で単にAgeと記述した場合には引数のAgeを意味することになり、クラスレベルの変数Ageにはアクセスできません。これは適用範囲内に同じ名前のものが存在する場合にはより適用範囲の狭い方と解釈されると言う性質によります。これが適用範囲のシャドウです。この場合コンストラクタの内部でクラスレベルの変数のAgeにアクセスするにはMe.Ageなどと記述します。

適用範囲のよるシャドウについては初級講座第8回 もっと変数でもふれています。

 

3.プロテクトメンバ

 

前回も少し書きましたが適用範囲がProtectedであるメンバは定義されているクラス以外では派生クラスからアクセスできませんから、派生クラスで使用されることが前提となっていると言うことができます。特にProtectedでかつOverridableになっているものは派生クラスでオーバーライドすることがはじめから想定されているのです。適用範囲がProtectedであるメンバのことを「プロテクトメンバ」と呼びます。

MSDNライブラリをみるとプロテクトメンバの数の多さに驚くかもしれません。クラスによってはPublicであるメンバよりもプロテクトメンバの方が多いものもあります。

プロテクトメンバの多くは、内部の処理に使用するメソッドで、派生クラスからも呼び出せた方が便利であったり派生クラスでオーバーライドする可能性があるものです。プロテクトメンバのもう1つの大きなグループは名前が「On」から始まっているメソッドです。これらは継承階層の中でイベントのように使用されます。また、実際にイベントを発生させる役目もあります。

もちろん、名前が「On」から始まると言う目印は文法的なものではないので、クラスの設計者はこの慣習を無視することもできます。しかし、.NET Frameworkの標準のクラスライブラリではこの慣習が守られているようです。

たとえば、MSDNライブラリでTextBoxのプロテクトメンバを見てみるとOnClickOnKeyPressOnTextChangedなどイベントに対応するかのように大量の「On」から始まるメソッドがあることがわかります。これらはオーバーライド可能であり派生クラス側で制御することができるようになっています。

これらのメソッドは実はイベントを発生させているだけで他には特に何もしません。名前を見れば想像がつくようにOnClickメソッドはClickイベントを発生させますし、OnKeyPressイベントはKeyPressイベントを発生させます。ということは、これらのメソッドを派生クラス側で変更することでイベントの発生を制御できるわけです。

そろそろ具体例を見ましょう。

次の例はTextBoxクラスを継承したTextBoxExクラスです。OnKeyPressメソッドをオーバーライドしていますが、特に処理は追加していません。

VB.NET2002対応 VB.NET2003対応 VB2005対応 VB2008対応

Public Class TextBoxEx

    Inherits TextBox

    Protected Overrides Sub OnKeyPress(ByVal e As System.Windows.Forms.KeyPressEventArgs)
        MyBase.OnKeyPress(e)
    End
Sub

End
Class

■リスト10

念のためにKeyPressイベントがちゃんと発生することを確認しておきましょう。プログラムをビルドしてツールバーからTextBoxExを1つフォームに貼り付けてください。VB.NET2003以前をお使いの方は手動でツールボックスにコンポーネントを追加する必要があります。

そして、フォームに次のようにプログラムします。

VB.NET2002対応 VB.NET2003対応 VB2005対応 VB2008対応

Private Sub TextBoxEx1_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles TextBoxEx1.KeyPress

    MsgBox("押されたキー:" & e.KeyChar)

End
Sub

■リスト11

これで実行すると少し邪魔ですが、TextBoxExにキーを入力するたびに入力したキーが表示されます。つまりKeyPressイベントは正確に動作していることになります。

ところで、[ESC]キーを押してもKeyPressイベントが発生すると言うことをみなさんはご存じだったでしょうか?ためしに、TextBoxExで[ESC]キーを押してみてください。少し文字化けしたかのようなメッセージになりますが、メッセージが表示されることからイベント自体は発生していることがわかります。

ここでTextBoxExのプログラムを修正して[ESC]キーではKeyPressイベントが発生しないようにしてみましょう。

次のようになります。

VB.NET2002対応 VB.NET2003対応 VB2005対応 VB2008対応

Public Class TextBoxEx

    Inherits TextBox

    Protected
Overrides Sub OnKeyPress(ByVal e As System.Windows.Forms.KeyPressEventArgs)

        If
e.KeyChar = Chr(Keys.Escape)
Then
           
e.Handled = True
            Return
       
End If

       
MyBase.OnKeyPress(e)

    End
Sub

End
Class

■リスト12

これで実行すると今度は[ESC]キーではイベントが発生しなくなります。

このようにして継承を利用して派生クラス側でイベントの発生を制御することが可能になるのです。

 

さて、Paintイベントはコントロールの外見を描画するイベントです。ですから、このイベントに対応するプロテクトメソッドであるOnPaintをオーバーライドするとコントロールの外見をかなりの自由度で変更することができます。

コントロールの継承についてはいろいろと考慮しなければいけない点もあるので、詳しくは別の機会に説明したいと思っていますが、参考までにButtonクラスを継承して丸い外見にするEllipseButtonクラスを紹介しておきます。

VB.NET2002、VB.NET2003で使用する場合はイベントハンドルの記述法や型変換の部分を少し変える必要がありますが、大部分は同じコードが通用します。

VB2005対応 VB2008対応

Imports System.Drawing.Drawing2D

Public Class EllipseButton

    Inherits Button

    Protected
Overrides Sub OnPaint(ByVal pevent As System.Windows.Forms.PaintEventArgs)

        Dim
Color1 As Color
        Dim Color2 As Color

        If
IsMouse
Then
            If Control.MouseButtons = Windows.Forms.MouseButtons.Left Then
               
Color1 = Color.Orange
                Color2 = Color.Beige
           
Else
               
Color1 = Color.Beige
                Color2 = Color.Orange
            End
If
        Else
           
Color1 = Color.Aqua
            Color2 = Color.Blue
        End
If

       
Dim b1 As New LinearGradientBrush(pevent.ClipRectangle, Color1, Color2, LinearGradientMode.ForwardDiagonal)
        Dim b2 As New LinearGradientBrush(pevent.ClipRectangle, Color2, Color1, LinearGradientMode.ForwardDiagonal)

        pevent.Graphics.FillRectangle(b1, pevent.ClipRectangle)

        Dim
InnerRect As RectangleF = pevent.ClipRectangle
        InnerRect.Inflate(-5, -5)
        pevent.Graphics.FillEllipse(b2, InnerRect)

        Dim
sf As New StringFormat
        sf.Alignment = StringAlignment.Center
        sf.LineAlignment = StringAlignment.Center

        Dim TextBrush As New SolidBrush(Me.ForeColor)
        pevent.Graphics.DrawString(Me.Text, Me.Font, TextBrush, InnerRect, sf)

    End
Sub
    Private Function ToRectangleF(ByVal Rect As Rectangle) As RectangleF

        Return
New RectangleF(Rect.Left, Rect.Top, Rect.Width, Rect.Height)

    End
Function
    Dim IsMouse As Boolean

   
Private Sub EllipseButton_MouseEnter(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.MouseEnter
        IsMouse =
True
   
End Sub
    Private Sub EllipseButton_MouseLeave(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.MouseLeave
        IsMouse =
False
   
End Sub
    Private Sub EllipseButton_Resize(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Resize

        Dim Path As New GraphicsPath
        Path.AddEllipse(Me.ClientRectangle)
        Me.Region = New Region(Path)

    End
Sub

End
Class

■リスト13

このボタンを利用してフォーム側でもある程度作りこめばかなり変わった外観のアプリケーションを作成することができます。以下はその例です。

■画像5:オーナードローを活用するとこのような画面も作成できる。

OnPaintなど描画関連の仕組みをオーバーライドなどの手段でカスタマイズすることをオーナードローまたはオーナー描画とよびます。独自の外観のフォームやコントロールに興味がある方はオーナードローまたはオーナー描画をキーワードに調べてみてください。

 

 

4.機能の削除

 

ここまでで基底クラスに機能を追加する方法と、基底クラスの機能を変更する方法を説明しました。また、その際に注意すべきMeMyClassの用法についてもふれました。

今度は基底クラスの機能を削除する説明をします。たとえば、基底クラスのメンバに直接アクセスされたくない場合や、インテリセンスの一覧から基底クラスのメンバを隠したい時などに基底クラスの機能(の呼び出し口)を派生クラス側では削除したいことがあります。厳密には「削除」というよりも継承したくないし、隠したいということになります。

※単純に「隠蔽」と言うと「シャドウ」のことを指すので混同しないように注意してください。

ところが、基底クラスのメンバを削除したり、継承しなかったり、アクセスできなくすると言う機能はありません。

派生クラス側で基底クラスのメンバと同じ名前のメンバのシャドウを作成してPrivateにすればアクセスできないようにできそうに思えるかもしれませんがうまくいきません。 この場合、VBは基底クラスのメンバに直接アクセスします。

たとえば、次のクラスは背景色を変更できないテキストボックスを作成することを意図して、TextBoxクラスのBackColorプロパティをアクセス不能にしようとしていますが、実際にはBackColorプロパティにはアクセス可能ですし、プロパティウィンドウで変更することも普通にできます。もちろん例外も発生しません。

VB.NET2002対応 VB.NET2003対応 VB2005対応 VB2008対応

Public Class TextBoxEx

    Inherits TextBox

    Private Shadows Property BackColor() As System.Drawing.Color
       
Get
           
Throw New InvalidOperationException("BackColorプロパティはアクセス禁止!")
        End
Get
       
Set(ByVal value As System.Drawing.Color)
            Throw New InvalidOperationException("BackColorプロパティはアクセス禁止!")
        End
Set
   
End Property

End
Class

■リスト14:この例は意図したとおりに作用しない。

これはShadowsはたまたま名前が同じだけの別のメンバとして認識されるので、基底クラスのBackColorプロパティがオーバーライドされていないと判断されるためです。オーバーライドされていないメンバは既定で、基底クラスのメンバに直接アクセスできます。これは今までのManmalクラスの例でもあきらかです。

なお、適用範囲をPublicにすれば、アクセス可能でも例外が発生するようにすることはできます。

 

これらのことを考えると普通は基底クラスのメンバへのアクセスをできなくするという計画は諦めた方がよいのですが、実はトリッキーな手段を使って疑似的に実現することができます。対象のメンバと同じ名前のイベントを実装してしまうのです。

VB.NET2002対応 VB.NET2003対応 VB2005対応 VB2008対応

Public Class TextBoxEx

    Inherits TextBox

    Public Shadows Event BackColor()

End Class

■リスト15

このように書くと、通常の方法ではプログラムからTextBoxExクラスのBackColorプロパティにはアクセスできなくなります。

■画像6:インテリセンスからBackColorプロパティを隠蔽

インテリセンス上に表示されなくなるだけではなく、BackColorプロパティにアクセスするコードはビルドエラーになります。ただし、プロパティウィンドウは誤魔化せません。

それにこの方法ですとBackColorという名前の実体のないイベントが追加されてしまいスマートではありません。ここでは基底クラスのメンバをアクセス不能にできると言う例として紹介はしましたが、万一採用される場合はデメリットもよく検討してください。

 

5.継承の制御

 

最後に継承を強制したり、継承できなくする制御について簡単に説明します。

MustInheritを使用するとクラスレベルでかならず継承しなければならないようにすることが可能です。

VB.NET2002対応 VB.NET2003対応 VB2005対応 VB2008対応

Public MustInherit Class Test

    Public
Sub Say()
        MsgBox("Hello")
    End
Sub

End
Class

■リスト16

MustInheritをつけるとクラスはインスタンス化できなくなります。共有メンバの呼び出しだけはできますが、それ以外は継承して派生クラスをつくることしか使い道がありません。このようなクラスのことを「抽象クラス」と呼びます。抽象クラスはポリモーフィズムを実現するために使用します。

同じ理由でメンバ単位でオーバーライドしなければ使えないようにすることもできます。これにはMustOverrideを使用します。こちらは抽象メソッドや抽象プロパティと呼びます。

使えないクラスやメソッドやプロパティなど何のために宣言するのか疑問に思われて当然です。この事情が分かればオブジェクト指向にもだいぶ慣れてきたことになるでしょう。詳しくは次回説明する予定です。

 

逆にNotInheritableを使用すると継承不可なクラスを作成することができます。

VB.NET2002対応 VB.NET2003対応 VB2005対応 VB2008対応

Public NotInheritable Class Test

    Public
Sub Say()
        MsgBox("Hello")
    End
Sub

End
Class

■リスト17

これは、クラスが他のクラスとの関係を前提としているなど、派生クラスを作成してもうまく動作させることができない場合や、制御が複雑すぎるなど何らかの理由で混乱をさける目的で使用したりするようです。メンバのレベルでオーバーライドを不可能にするにはNotOverridableを使用します。

NotInheritableは指定してもしなくてもそのクラス自体の機能や性質にはまったく影響はありません。継承を不可能にしてまで何かを守ろうとするかは設計者の判断に委ねられます。MSDNライブラリをよく見ると.NET Frameworkには継承不可能なクラスがたくさんあります。たとえばDBNullクラスやAppDomainクラスは継承できませんし、理由もすぐに想像できます。DBNullクラスは唯一性が重要なので派生クラスの存在自体を否定する必要がありますし、AppDomainクラスはアプリケーション境界として働きますので派生クラスで妙な実装をされて.NET Frameworkの仕組みが台無しにされるのを防いでいるのでしょう。とは言え、この目的であればメソッドレベルでNotOverrideにするだけで足りますからクラス自体がNotInheritableになっている事情はいまいちわかりにくいです。

他にもFileInfoクラスやStringクラスなどさまざまなクラスが継承不可能です。VBの継承機能はVB.NET 2002から加わった機能なのですが、当時私はまっさきにStringクラスの継承にチャレンジしました。自分で文字列に機能を追加できるとしたらいろいろな使い道がありそうだと思ったからです。しかし、残念ならがStringクラスは継承できないのでした。VBの言語仕様に密接に関連しているからでしょうか?できれば継承したいものです。

VB2008からは継承の他に、拡張メソッドと言う機能を使用してクラスに機能を追加できるようになりました。これでようやくStringクラスに機能を追加することができます。継承できないクラスに機能を追加する場合は拡張メソッドの使用を検討してください。

次の例はStringクラスに文字列をすべて全角に変換するToWideメソッドを追加します。

VB2008対応

Module ExtensionMethods

    <System.Runtime.CompilerServices.Extension()> _
    Public Function ToWide(ByVal s As String) As String
        Return StrConv(s, VbStrConv.Wide)
    End Function

End Module

■リスト18

このモジュールが適用範囲にあれば、ToWideメソッドが使用可能になります。

■画像7:拡張メソッドの使用

 

最後に1つだけ注意しておくことがあります。継承は大変便利な機能で使い方次第でプログラムにかかる時間が大幅に短縮できる可能性がありますが、実際に継承を使う際には本当に継承が必要であるかどうかよく検討してください。

ある程度の規模のプログラムになると、無計画に継承を使用していくとプログラムが複雑になり収拾がつかなくなってしまいます。一つだけ機能を変更するつもりで基底クラスを変更したら、思わぬところに影響がでるということは容易に想像ができます。基底クラスの修正は影響が大きいのです。

ですから、たとえば単にClassAClassBで共通の機能があるという理由だけで、ClassZを作成して、ClassAClassBはそれを継承すると言うようなことは避けるべきです。単に共通の機能があるというだけならば共通の機能にアクセスするためのシンプルなクラスを1つ作成する方が優れています。

 

博士のワンポイントレッスン
V太:博士。ぼくは偉大な発見をしてしまいました。
博士:なんじゃなんじゃ。突然何を言い出すんじゃ?!
今回、Protected Friendとか、Overridableとかメソッドやプロパティの宣言のときに使用するキーワードがたくさん登場しましたよね?
うん。そうじゃね…。
あれをいっぱいつけたらメソッドやプロパティの宣言がとんでもなく長くなるんです!
B子:ほほー。たとえば、Protected Friend Overridable Overloads Subとか?大分長いわよね。
もっともっと長くできます。
Protected Friend Overridable Overloads ReadOnly Propertyでどうかしら?かなり長いわよ。
まだまだ長くできます!!
うーーん。ShadowsOverridesは一緒に使えないしのぉ。ReadOnlyWriteOnlyも片方だけじゃろう。どんな風にやるのかね?
Default Protected Friend NotOverridable Overloads Overrides ReadOnly Propertyです!
確かに成立しておる。基底クラスに同じ名前で引数付きプロパティがあることが前提じゃな。

VB.NET2002対応 VB.NET2003対応 VB2005対応 VB2008対応

Public Class Test1

    Default
Protected Friend Overridable Overloads ReadOnly Property PropName(ByVal i As Integer) As
Integer
        Get
           
Return i
        End
Get
   
End Property

End
Class
Public Class Test2

    Inherits
Test1

    Default
Protected Friend NotOverridable Overloads Overrides ReadOnly Property PropName(ByVal i As Integer) As
Integer
        Get
           
Return MyBase.PropName(i)
        End
Get
   
End Property

End
Class

■リスト19