以下のようなクラス構造を作るとどう動作するのか、ちょっと自信が無かったので確認。
// 基底クラスであるインターフェースを実装
public class Animal : IHogerable
{
public void DoHoge()
{
Console.WriteLine("Animal");
}
}
// 派生クラスで同じインターフェースを実装
public class Dog : Animal, IHogerable
{
// new は無くても動作は同じ。new が無い場合は警告が出る。
public new void DoHoge()
{
Console.WriteLine("Dog");
}
}
public interface IHogerable
{
void DoHoge();
}
これらを IHogerable
型の変数経由で呼び出すとどうなるか?
もちろん、Animal
型の変数や Dog
型の変数経由で呼び出した場合はそれぞれのメソッドが呼ばれる。
var animal = new Animal();
animal.DoHoge(); // Animal.DoHoge が呼ばれる
var dog = new Dog();
dog.DoHoge(); // Dog.DoHoge が呼ばれる
foreach (var hogerable in new IHogerable[] { animal, dog })
{
hogerable.DoHoge(); // どっちのメソッドが呼ばれる???
}
実行結果
Animal
Dog
Animal
Dog
IHogerable
型の変数経由で呼び出した場合、インスタンスの型に応じて呼び出されるメソッドが変わる。
なお、Dog
クラスの宣言で明示的に : IHogerable
を書くか書かないかで動作が変わる。
public class Dog : Animal // IHogerable の実装を宣言しない
{
public new void DoHoge() // 単に Animal.DoHoge の実装を隠ぺいする
{
Console.WriteLine("Dog");
}
}
こうすると
実行結果
Animal
Dog
Animal
Animal
IHogerable
経由で呼び出した場合に Animal.DoHoge
の方が呼ばれるようになる。
つまり、Dog
クラスで IHogerable
の実装を上書きする意図があるかないかをくみ取ってくれる。
よく考えられているなぁ……
さらに確認した内容を追記します。
Q. インターフェースに複数のメンバーが定義されている場合は?
A. Dog
で隠ぺいした分はそちらが、隠ぺいしていない分は Animal
のメンバーが呼び出されます(つまり一部だけを隠ぺいすることが可能)
Q. Animal
クラスで暗黙的な実装と明示的な実装の両方がある場合は?
A. IHogerable
型経由で Dog
インスタンスのメンバーを呼び出した場合、以下の優先順で呼ばれます。直感通りです
-
Dog
クラスの明示的実装 -
Dog
クラスの隠ぺいしている暗黙的実装 -
Animal
クラスの明示的実装 -
Animal
クラスの暗黙的実装