次のC#クラス定義とコードがあるとします。
public class BaseClass
{
public virtual void MyMethod()
{
...do something...
}
}
public class A : BaseClass
{
public override void MyMethod()
{
...do something different...
}
}
public class B : BaseClass
{
public override void MyMethod()
{
...do something different...
}
}
public class AnotherObject
{
public AnotherObject(BaseClass someObject)
{
someObject.MyMethod(); //This calls the BaseClass method, unfortunately.
}
}
渡されたオブジェクトが実際にはBaseClassにあるものではなく、AまたはBのインスタンスであると仮定して、実際にAまたはBにあるMyMethod()を呼び出したいと思います。このようなことをするのに足りない:
public class AnotherObject
{
public AnotherObject(BaseClass someObject)
{
A temp1 = someObject as A;
if (A != null)
{
A.MyMethod();
}
B temp2 = someObject as B;
if (B != null)
{
B.MyMethod();
}
}
}
どうすればいいですか?
どのメソッドが呼び出されるかは、AnotherObjectコンストラクターに渡される型の多形によって決定されます。
AnotherObject a = new AnotherObject(new A()); // invokes A.MyMethod()
AnotherObject b = new AnotherObject(new B()); // invokes B.MyMethod()
AnotherObject c = new AnotherObject(new BaseClass()); //invokes BaseClass.MyMethod()
申し訳ありませんが、あなたは完全に間違っています。これは、仮想メソッドの全体的なポイントに反します。 someObject
がA
の場合、_A.MyMethod
_が呼び出されます。 someObject
がB
の場合、_B.MyMethod
_が呼び出されます。 someObject
がBaseClass
であり、BaseClass
から派生したタイプのインスタンスではない場合、_BaseClass.MyMethod
_が呼び出されます。
みんなのお気に入りの例を使ってみましょう:
_class Animal {
public virtual void Speak() {
Console.WriteLine("i can haz cheezburger?");
}
}
class Feeder {
public void Feed(Animal animal) { animal.Speak(); }
}
class Cat : Animal {
public override void Speak() { Console.WriteLine("Meow!"); }
}
class Dog : Animal {
public override void Speak() { Console.WriteLine("Woof!"); }
}
_
次に:
_Animal a = new Animal();
Animal c = new Cat();
Animal d = new Dog();
Feeder f = new Feeder();
f.Feed(a);
f.Feed(c);
f.Feed(d);
_
これは印刷されます:
_i can haz cheezburger?
Meow!
Woof!
_
繰り返しますが、これが仮想メソッドの要点です。
さらに、仕様に行くことができます。 10.6.3から(仮想メソッド)
仮想メソッドの呼び出しでは、その呼び出しが行われるインスタンスのrun-time typeが実際のメソッドの実装を決定します呼び出す。
(元の太字と斜体。)
正確に言うと、
N
という名前のメソッドがコンパイル時タイプA
および実行時タイプC
(R
はR
またはC
から派生したクラス)のインスタンスで引数リストC
を使用して呼び出された場合次のように処理されます。•最初に、過負荷解決が
C
、N
、およびA
に適用され、M
で宣言され、継承される一連のメソッドから特定のメソッドC
を選択します。これは§7.5.5.1で説明されています。•次に、
M
が非仮想メソッドの場合、M
が呼び出されます。•それ以外の場合、
M
は仮想メソッドであり、Rに関してM
の最も派生した実装が呼び出されます。
(太字はオリジナルではありません。)
次に、「M
の最も派生した実装」の定義が必要です。これは素晴らしい再帰的定義です:
クラス
M
に関する仮想メソッドR
の最も派生した実装は、次のように決定されます。•
R
にM
の導入仮想宣言が含まれている場合、これはM
の最も派生した実装です。•それ以外の場合、
R
にM
のオーバーライドが含まれている場合、これはM
の最も派生した実装です。•それ以外の場合、
M
に関するR
の最も派生した実装は、M
の直接基本クラスに関するR
の最も派生した実装と同じです。
したがって、上記の_Cat : Animal
_および_Dog : Animal
_の例では、Feeder.Feed(Animal)
へのパラメーターa
がCat
のインスタンスである場合、_Cat.Speak
_が最も派生した実装です。これが、「_Meow!
_」ではなく「_i can haz cheezburger?
_」が表示される理由です。
MyMethod()
が基本クラスで抽象である場合、派生クラスのバージョンが使用されます。したがって、基本クラスのインスタンスを呼び出す必要がない場合は、これがオプションになります。
static void Main(string[] args)
{
A classA = new A();
B classB = new B();
DoFunctionInClass(classA);
DoFunctionInClass(classB);
DoFunctionInClass(classA as BaseClass);
Console.ReadKey();
}
public static void DoFunctionInClass(BaseClass c)
{
c.MyMethod();
}
public abstract class BaseClass
{
public abstract void MyMethod();
}
public class A : BaseClass
{
public override void MyMethod()
{
Console.WriteLine("Class A");
}
}
public class B : BaseClass
{
public override void MyMethod()
{
Console.WriteLine("Class B");
}
}
AまたはBではなくBaseClassとして入力したため、baseclassはメソッド呼び出しの開始点です。
ジェネリックを使用してみてください。
public class AnotherObject
{
public AnotherObject<T>(T someObject) where T : BaseClass
{
someObject.MyMethod(); //This calls the BaseClass method, unfortunately.
}
}
これがコンストラクターでどれだけうまくいくかはわかりませんが、これを別のメソッドに移動できる可能性があります。
渡されたsomeObjectがクラスAの場合、基本クラスの実装ではなく、A.MyMethodが呼び出されます。 is キーワードも見てください。