Me, MyClass, MyBase 〜 MeとMyClassの違い

注意:
この文書は以前「.NETでいきまっしょい!」で公開していたものですが、公開以降メンテナンスされていません。 今や古い情報となった内容が記載されている場合があるのでご注意ください。

1.Me, MyClass, MyBase


 C++、VBや、VB.NET、C#にはクラスのインスタンスが自分自身とそのメンバを参照するためのキーワードがあります。 例えばC++ではthisポインタ、VBではMeキーワードなどです。 同様にC#ではthis及びbaseキーワード、VB.NETではVBから引き続きMeが使われ、新たにMyClassとMyBaseというキーワードが採用されました。 VB.NETとC#でMyBase / baseというキーワードが採用された背景には、共通言語仕様ではクラスの多重継承が認められていないために基底クラスは常に一つであるため、C++のように任意の基底クラスを参照するためにキャストを行う必要がなくなったからと考えられます。
 このようにVB.NETとC#の MyClass / this と MyBase / base はその機能や目的は同じであるといえます。 ですが、VB.NETにはMyClassに似た働きを持つMeキーワードが存在します。 MeとMyClassの機能の違いに言及する前に、Me・MyClass・MyBaseを使用した例によってその動作を見てみることにします。
Me, MyClass, MyBase
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
Imports System

' 基底クラス
Public Class BaseClass

    ' 基底クラスでのMethod()
    Protected Overridable Sub Method(ByVal keyword As String)

        Console.WriteLine("Method of BaseClass called by " + keyword)

    End Sub

End Class

' 派生クラス
Public Class DerivedClass
    Inherits BaseClass

    ' 派生クラスでのMethod()
    Protected Overrides Sub Method(ByVal keyword As String)

        Console.WriteLine("Method of DerivedClass called by " + keyword)

    End Sub


    ' 各キーワードでインスタンスのメソッドを呼び出すためのメソッド
    Public Sub CallMethods()

        ' MyBase による呼び出し
        MyBase.Method("MyBase")

        ' MyClass による呼び出し
        MyClass.Method("MyClass")

        ' Me による呼び出し
        Me.Method("Me")

    End Sub

End Class

' アプリケーションのエントリーポイント
Module MeMyclassMybase

    Sub Main()

        ' 派生クラスのインスタンスを作成
        Dim inst As New DerivedClass()

        inst.CallMethods()

    End Sub

End Module
出力結果
Method of BaseClass called by MyBase
Method of DerivedClass called by MyClass
Method of DerivedClass called by Me
Press any key to continue

 この結果から分かるとおり、MyBaseはそのインスタンスの基底クラスのメンバを参照しているのに対し、MyClass及びMeはそのインスタンス自身のクラスのメンバを参照していることがわかります。 では、MyClassとMeは同じものといえるのでしょうか。 下位互換性のためにMeが残されたのでしょうか。 それともキーワードが二つあるからには何らかの違いがあるのでしょうか。

2.MeとMyClassの違い


 実際のところ、全く意味の同じキーワードが二種類存在するのは下位互換性のためだけではなく、ちゃんとした違いがあります。 ほとんどの機能はMeもMyClassも大差ないのですが、メソッドの呼び出しの時にはその違いがあらわれます。 その違いがあらわれるようなサンプルコードを次に示します。
MeとMyClassの違い
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
Imports System

' 基底クラス
Public Class BaseClass

    ' 基底クラスでのMethod()
    Protected Overridable Sub Method(ByVal keyword As String)

        Console.WriteLine("Method of BaseClass called by " + keyword)

    End Sub

    ' 各キーワードでインスタンスのメソッドを呼び出すためのメソッド
    Public Sub CallMethods()

        ' MyClass による呼び出し
        MyClass.Method("MyClass")

        ' Me による呼び出し
        Me.Method("Me")

    End Sub

End Class

' 派生クラス
Public Class DerivedClass
    Inherits BaseClass

    ' 派生クラスでのMethod()
    Protected Overrides Sub Method(ByVal keyword As String)

        Console.WriteLine("Method of DerivedClass called by " + keyword)

    End Sub

End Class

' アプリケーションのエントリーポイント
Module MeMyclassMybase

    Sub Main()

        ' 派生クラスのインスタンスを作成
        Dim inst As New DerivedClass()

        inst.CallMethods()

    End Sub

End Module
出力結果
Method of BaseClass called by MyClass
Method of DerivedClass called by Me
Press any key to continue

 今回は先ほどとは異なり、CallMethods()を基底クラスに配置しました。 そして、派生クラスDerivedClassのインスタンスを作成し、そのインスタンスからCallMethods()を呼び出しています。 結果を見てわかるとおり、MeとMyClassでは異なる文字列を出力していることがわかります。 つまり、Me.Method()はDerivedClass.Method()を呼び、MyClass.Method()はBaseClass.Method()を呼び出しています。
 これは何を意味しているかというと、MyClassはインスタンスのクラスがどのクラスであるかに関わらず、確実にMyClassが用いられたクラスのメソッドを呼び出しているのに対し、Meの場合はMeが用いられたクラスが何であるかに関わらず、インスタンスのクラスのメソッド、つまり派生クラスでオーバーライドされたメソッドを呼び出していることになります。
 さらに、派生クラスで基底クラスのメソッドをシャドウするとその挙動は変化します。 次のコードはそれを行ったものです(DerivedClass.Method()のOverridesキーワードをShadowsキーワードに変えています)。
MeとMyClassの違い その2
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
Imports System

' 基底クラス
Public Class BaseClass

    ' 基底クラスでのMethod()
    Protected Overridable Sub Method(ByVal keyword As String)

        Console.WriteLine("Method of BaseClass called by " + keyword)

    End Sub

    ' 各キーワードでインスタンスのメソッドを呼び出すためのメソッド
    Public Sub CallMethods()

        ' MyClass による呼び出し
        MyClass.Method("MyClass")

        ' Me による呼び出し
        Me.Method("Me")

    End Sub

End Class

' 派生クラス
Public Class DerivedClass
    Inherits BaseClass

    ' 派生クラスでのMethod()
    Protected Shadows Sub Method(ByVal keyword As String)

        Console.WriteLine("Method of DerivedClass called by " + keyword)

    End Sub

End Class

' アプリケーションのエントリーポイント
Module MeMyclassMybase

    Sub Main()

        ' 派生クラスのインスタンスを作成
        Dim inst As New DerivedClass()

        inst.CallMethods()

    End Sub

End Module
出力結果
Method of BaseClass called by MyClass
Method of BaseClass called by Me
Press any key to continue

 今度はMeもMyClassもBaseClass.Method()を呼び出しています。 このように、メソッドの呼び出しではMeとMyClassは多少挙動が異なるので注意が必要です。 通常の使用方法ならMyClassを用いた方がよけいなバグを生じる可能性は少ないと思います(しかし、VB時代からの習慣で、ついMeを使いたくなってしまうのがホントのところです・・・)。