【Unity C# 復習】virtual/overrideのおさらい
はじめに
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();
実行結果
ClassA の Foo() メソッドが実行されます。
ケース2 : ClassA2 a = new ClassA()
次に、 ClassA を継承した ClassA2 を作成します。
public class ClassA2 : ClassA
{
}
メソッド実行
そして、次のようなメソッド呼び出しを書いてみます。
ClassA2 a = new ClassA();
a.Foo();
結果
これはコンパイルエラーになり、実行することができません。
ClassA は ClassA2 を知らないので、コンパイルエラーになります。
ケース3 : ClassA a = new ClassA2()
今度は、以下のようなメソッド呼び出しを書いてみます。
ClassA a = new ClassA2();
a.Foo();
結果
ClassA の Foo メソッドが実行されます。
ClassA2 は ClassA を知っており、コンパイルエラーにはなりません。
ケース4 : 派生クラスにFooメソッドを定義
ここで、新たに ClassA2 にも Foo というメソッドを定義してみます。
public class ClassA2 : ClassA
{
public void Foo()
{
Debug.Log("ClassA2 :: Foo()");
}
}
ClassA も Foo メソッドを持っています。
public class ClassA
{
public void Foo()
{
Debug.Log("ClassA :: Foo()");
}
}
メソッド実行
そして、以下のような Foo メソッド呼び出しを実行します。
ClassA a = new ClassA2();
a.Foo();
結果
ClassA の Foo メソッドが実行されます。ClassA2 の Foo メソッドは実行されません。
ClassA2 のメソッドが呼ばれるようにしたい場合、ClassA の Foo にはvirtualをつける必要があります。
ケース5 : virtual/override
ClassAのFooにvirtual, ClassA2のFooにoverrideをつけます。
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();
結果
ClassA2 の Foo メソッドが実行されます。
しかし、今度はClassAのFooメソッドが実行されなくなってしまいました。
ClassA と ClassA2 両方の Foo メソッドを実行したい場合は、base.Foo() を呼びます。
ケース6 : base.Foo()
ClassA2 の Foo メソッドの中で、base.Foo() を呼び出します。
public class ClassA2 : ClassA
{
public override void Foo()
{
base.Foo();
Debug.Log("ClassA2 :: Foo()");
}
}
メソッド実行
そして、以下のようなメソッド実行を呼び出してみます。
ClassA a = new ClassA2();
a.Foo();
結果
ClassA2 の Foo メソッドから ClassA の Fooメソッドが呼ばれます。
ケース7 : overrideを書き忘れた場合
ClassA2にoverrideを書き忘れた場合はClassA2のFooは実行されません。
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