virtualとoverrideによるメソッドの上書き

プログラミングC# 第6版 4.3.2


さてここもC++経験者は注意が必要です。virtualの動作がC++C#で少し違います。

まずは以下の例を確認してみます。

using System;

class CBase {
    // 基底クラスのメソッドにvirtualをつける
    public virtual void Foo () {
        Console.WriteLine("CBase.Foo");
    }
    public void Call () {
        Foo();
    }
}
class CClass : CBase {
    public new void Foo () {
        Console.WriteLine("CClass.Foo");
    }
}

class Program {
    static void Main () {
        CClass cobj1 = new CClass();
        CBase cobj2 = cobj1;
        
        cobj1.Call();
        cobj2.Call();
    }
}

さてこれの実行結果はどうなると思いますか?C++経験者ならこう思うでしょう。

基底クラスのFooにvirtualが付いてるから仮想関数になるので基底クラスCallからのFoo呼び出しは派生クラスのFooが使われるはず。

よって出力は「CClass.Foo」が正解!

・・・ところがどっこい実行してみると

$ main
CBase.Foo
CBase.Foo

となります。

C#のvirtualは仮想関数として使われますよという宣言のみで、実際にオーバーライドしたければ派生クラス側で上書きしますという宣言を書かなければなりません。

それがoverrideキーワードです。どこに書くかと言うと、今newキーワードが書かれている部分ですね。

using System;

class CBase {
    // 基底クラスのメソッドにvirtualをつける
    public virtual void Foo () {
        Console.WriteLine("CBase.Foo");
    }
    public void Call () {
        Foo();
    }
}
class CClass : CBase {
    // overrideキーワードを指定する
    public override void Foo () {
        Console.WriteLine("CClass.Foo");
    }
}

class Program {
    static void Main () {
        CClass cobj1 = new CClass();
        CBase cobj2 = cobj1;
        
        cobj1.Call();
        cobj2.Call();
    }
}
$ main
CClass.Foo
CClass.Foo

これでめでたくC++と同じ動きになりました。

ちなみにoverrideは逆に言えばvirtual指定されてないメソッドには使用できないので、基底クラスのFooからvirtualを削除するとコンパイルエラーが出ます。

class CBase {
    // virtualを消しちゃう
    public void Foo () {
        Console.WriteLine("CBase.Foo");
    }
    public void Call () {
        Foo();
    }
}
class CClass : CBase {
    // overrideキーワードは指定したまま
    public override void Foo () {
        Console.WriteLine("CClass.Foo");
    }
}
$ csc main.cs
Microsoft (R) Visual C# 2010 Compiler version 4.0.30319.1
Copyright (C) Microsoft Corporation. All rights reserved.

main.cs(12,23): error CS0506: 'CClass.Foo()': virtual、abstract または override に設定されていないため、継承されたメンバー 'CBase.Foo()' をオーバーライドできません。
main.cs(4,14): (以前のエラーに関連するシンボルの位置)

まとめると、「C++のvirtual」は「C#のvirtual+override」と同じ意味になる。という感じでしょうか。