オーバーライドされた仮想メソッドが、それをオーバーライドするクラスではなく、それを導入するクラスに属していることを理解すると、クラス メンバーがバインドされる方法を理解しやすくなります。オブジェクトを使用する場合を除きdynamic
、C# のすべてのバインドはコンパイル時に解決されます。aBaseClass
が仮想メソッドを宣言しfoo
、 をDerivedClass:BaseClass
オーバーライドする場合、型の変数foo
を呼び出そうとするコードは常に仮想メソッド "slot" にバインドされ、それが実際のメソッドを指します。foo
BaseClass
BaseClass.foo
DerivedClass.foo
C# では、C++ とは異なり、ジェネリック型のメンバーは具体的なジェネリック型ではなく、ジェネリックの制約に従ってバインドされるため、この理解はジェネリックを扱う場合に特に重要です。たとえば、 methodSubDerivedClass:DerivedClass
を作成したと、 new virtual
methodfoo()
を定義したがあったとしDoFoo<T>(T param) where T:BaseClass {param.foo();}
ます。メソッドが として呼び出された場合でも、呼び出しparam.foo()
は にバインドされます。を呼び出す前にパラメーターが にキャストされた場合、呼び出しは にバインドされますが、コンパイラーは、それがより具体的なものになる時期を判断できず、 に存在しないものにはバインドできません。BaseClass.foo
DoFoo<SubDrivedClass>(subDerivedInstance)
SubDerivedClass
foo
SubDrivedClass.foo()
DoFoo<T>
T
BaseClass
BaseClass
ちなみに、クラスが基本クラスのメンバーを同時にオーバーライドし、新しいメンバーを作成できると便利な場合があります。たとえば、ReadableFoo
読み取り専用の抽象プロパティを持つ抽象基本クラスがある場合、クラスMutableFoo
がそのプロパティのオーバーライドを提供し、同じ名前で読み書き可能なプロパティを定義できると便利です。残念ながら、.net ではそれが許可されていません。このような制限がある場合、別の名前のメソッドReadableFoo
を呼び出して値を取得する、具体的な非仮想の読み取り専用プロパティを提供するのが最善の方法かもしれません。protected abstract
そうすれば、派生クラスは読み取り専用プロパティを読み取り/書き込みプロパティ (読み取り用に同じ仮想メソッドを呼び出すか、書き込み用に新しい (おそらく仮想) メソッドを呼び出す) でシャドウすることができます。
(以下は未確認)
class BaseClass
{
public virtual void foo() {Console.WriteLine("BaseClass.Foo");
}
class DerivedClass:BaseClass
{
public override void foo() {Console.WriteLine("Derived.Foo");
}
class SubDerivedClass:DerivedClass
{
public new virtual void foo() {Console.WriteLine("SubDerived.Foo");
}
class MegaDerivedClass:SubDerivedClass
{
public override void foo() {Console.WriteLine("MegaDerived.Foo");
}
void DoFoo1<T>(T param) where T:BaseClass
{
param.foo();
}
void DoFoo1<T>(T param) where T:SubDerivedClass
{
param.foo();
}
void test(void)
{
var megaDerivedInstance = new MegaDerivedClass();
DoFoo1<MegaDerivedClass>(megaDerivedInstance);
DoFoo2<MegaDerivedClass>(megaDerivedInstance);
}
SubDerivedClass には、 と の 2 つの仮想foo()
メソッドがBaseClass.foo()
ありSubDerivedClass.foo()
ます。AMegaDerivedClass
には同じ 2 つのメソッドがあります。SubDerivedClass()
オーバーライドしようとする派生クラスfoo
はオーバーライドされSubDerivedClass.foo()
、影響を与えないことに注意してくださいBaseClass.foo()
。上記の宣言では、 の派生物はSubDerivedClass
オーバーライドできませんBaseClass.Foo
。SubDerivedClass
また、インスタンスまたはそのサブクラスをキャストするDerivedClass
か、仮想メソッドを呼び出し用BaseClass
に公開することにも注意してください。BaseClass.foo
ちなみに、 のメソッド宣言が でSubDerivedClass
あった場合friend new virtual void foo() {Console.WriteLine("SubDerived.Foo");
、同じアセンブリ内の他のクラスがオーバーライドすることはできませんBaseClass.foo()
(オーバーライドしようとすると がオーバーライドさfoo()
れるためSubDerivedClass.foo()
) 。SubDerivedClass
SubDerivedClass.foo()
BaseClass.foo()