📝

【Unity C# 復習】virtual/overrideのおさらい

2021/01/12に公開

はじめに

C#の復習もかねて、基底クラス・派生クラスのメソッド呼び出しにまとめたいと思います。

記事中のクラス図は一般的なクラス図と書き方が異なるものですが、ご了承ください。

実行環境は ゲームエンジンUnity の C# を想定しています。

ケース1 : ClassA a = new ClassA()

ClassA というクラスを作成してみます。

public class ClassA
{
    public void Foo()
    {
        Debug.Log("ClassA :: Foo()");
    }
}

メソッド実行

そして、以下のようなメソッドを呼び出しを実行します。

ClassA a = new ClassA();
a.Foo();

実行結果

ClassAFoo() メソッドが実行されます。

ケース2 : ClassA2 a = new ClassA()

次に、 ClassA を継承した ClassA2 を作成します。

public class ClassA2 : ClassA
{
}

メソッド実行

そして、次のようなメソッド呼び出しを書いてみます。

ClassA2 a = new ClassA();
a.Foo();

結果

これはコンパイルエラーになり、実行することができません。

ClassAClassA2 を知らないので、コンパイルエラーになります。

ケース3 : ClassA a = new ClassA2()

今度は、以下のようなメソッド呼び出しを書いてみます。

ClassA a = new ClassA2();
a.Foo();

結果

ClassAFoo メソッドが実行されます。

ClassA2ClassA を知っており、コンパイルエラーにはなりません。

ケース4 : 派生クラスにFooメソッドを定義

ここで、新たに ClassA2 にも Foo というメソッドを定義してみます。

public class ClassA2 : ClassA
{
    public void Foo()
    {
        Debug.Log("ClassA2 :: Foo()");
    }
}

ClassAFoo メソッドを持っています。

public class ClassA
{
    public void Foo()
    {
        Debug.Log("ClassA :: Foo()");
    }
}

メソッド実行

そして、以下のような Foo メソッド呼び出しを実行します。

ClassA a = new ClassA2();
a.Foo();

結果

ClassAFoo メソッドが実行されます。ClassA2Foo メソッドは実行されません。

ClassA2 のメソッドが呼ばれるようにしたい場合、ClassAFoo にはvirtualをつける必要があります。

ケース5 : virtual/override

ClassAFoovirtual, ClassA2Foooverrideをつけます。

public class ClassA
{
    public virtual void Foo()
    {
        Debug.Log("ClassA :: Foo()");
    }
}
public class ClassA2 : ClassA
{
    public override void Foo()
    {
        Debug.Log("ClassA2 :: Foo()");
    }
}

メソッド実行

そして以下のようなメソッド呼び出しを実行します。

ClassA a = new ClassA2();
a.Foo();

結果

ClassA2Foo メソッドが実行されます。

しかし、今度はClassAFooメソッドが実行されなくなってしまいました。
ClassAClassA2 両方の Foo メソッドを実行したい場合は、base.Foo() を呼びます。

ケース6 : base.Foo()

ClassA2Foo メソッドの中で、base.Foo() を呼び出します。

public class ClassA2 : ClassA
{
    public override void Foo()
    {
        base.Foo();
        Debug.Log("ClassA2 :: Foo()");
    }
}

メソッド実行

そして、以下のようなメソッド実行を呼び出してみます。

ClassA a = new ClassA2();
a.Foo();

結果

ClassA2Foo メソッドから ClassA の Fooメソッドが呼ばれます。

ケース7 : overrideを書き忘れた場合

ClassA2overrideを書き忘れた場合はClassA2Fooは実行されません。

public class ClassA
{
    public virtual void Foo()
    {
        Debug.Log("ClassA :: Foo()");
    }
}
public class ClassA2 : ClassA
{
    public void Foo()
    {
        Debug.Log("ClassA2 :: Foo()");
    }
}

メソッド実行

ClassA a = new ClassA2();
a.Foo();

結果

ClassA のメソッドが実行されます。

まとめ

これは私個人の考えですが、メソッドにvirtualをつけるかどうかは以下のように判断すると良さそうです。

  • 派生先で挙動が変わる可能性があるメソッドに関してはvirtualつける
  • 派生先でも挙動が変わらないメソッドに関してはvirtualつけない

Discussion