为什么接口成员没有映射到派生类的非静态方法



我编写了一个接口和一个基类A实现它,然后是一个派生类B

interface ITest
{
void fn();
}
class A: ITest
{ 
void ITest.fn()
{
Console.WriteLine("fn in A");
}
}
class B: A
{
public void fn()
{
Console.WriteLine("fn in B");
}
}
class Program
{
static void Main(string[] args)
{
ITest tan = new B();
tan.fn();
Console.Read();
}
}

结果显示调用了A中的fn。但根据接口映射规则,我颇为不解:

  1. 如果 S 包含显式接口成员的声明 匹配 I 和 M 的实现,则此成员是 实施 I.M.

  2. 否则,如果 S 包含 匹配 M 的非静态公共成员,则此成员是 实施 I.M.

对于B的实例,它与规则 1 不匹配,没有显式接口成员实现。 但是,它有自己的非静态公共成员fn,我认为这与规则 2 匹配。所以tan.fn()应该调用该方法fnB,没有必要在其基类A中寻找另一个fn

怎么了?

(解决这个问题将有助于我理解接口重新实现(

因为它是一个显式接口实现。隐式实现接口使该方法在 B 中也可见。

此外,为了能够在 B 中覆盖它,您需要将其设置为虚拟。

规则2 不适用于 B,因为规则 1 适用于 B。有一个显式接口实现,即从 A 继承的接口实现。

按如下所示更改代码:

class A : ITest
{ 
public virtual void fn()
{
Console.WriteLine("fn in A");
}
}
class B : A
{
public override void fn()
{
Console.WriteLine("fn in B");
}
}

或者,如果确实要显式实现接口,则可以让实现调用可在 B 中重写的受保护虚拟方法。

class A : ITest
{ 
void ITest.fn()
{
FnCore();
}
protected virtual void FnCore()
{
Console.WriteLine("fn in A");
}
}
class B : A
{
public override void FnCore()
{
Console.WriteLine("fn in B");
}
}

正如Sergey.quixoticaxis.Ivanov指出的那样,你也可以通过更改B的声明来重新实现接口,如下所示:

class B : A, ITest

但是,这有一个很大的缺点:该方法不像您通常期望的那样是多态的。实际上,此代码将为您提供预期的结果:

static void Main(string[] args)
{
ITest tan = new B();
tan.fn();
}

鉴于您的接口是在 A 中显式实现的,下面的代码无法编译。但是,如果它是隐式实现的,当 B 只是重新实现接口而不覆盖虚拟基方法时,下面的代码仍然会输出 "fn in A":

static void Main(string[] args)
{
A tan = new B();
tan.fn();
}

好的,这里有一些带有中间语言的代码,它确实生成和注释:

public interface II
{
void f();
}
#region Explicit
public class AExplicit : II
{
void II.f() => Console.WriteLine(nameof(AExplicit));
}  
public class BExplicitIndependent : AExplicit
{
// void II.f() => Console.WriteLine("BExplicit"); // won't compile
// f() does not hide anything - it's completely new method
public void f() => Console.WriteLine("B that defines an independent method void f()");
}
public class BExplicitReImplemented : AExplicit, II
{
void II.f() => Console.WriteLine(nameof(BExplicitReImplemented));
}
#endregion Explicit
#region Implicit
public class AImplicit : II
{
public void f() => Console.WriteLine(nameof(AImplicit));
}

public class BImplicitIndependent : AImplicit
{
// WARNING: BImplicitReImplemented.f()' hides inherited member 'AImplicit.f()'. Use the new keyword if hiding was intended.
public void f() => Console.WriteLine("B that defines an independent method void f()");
}
public class BImplicitReImplemented : AImplicit, II
{
public void f() => Console.WriteLine(nameof(BImplicitReImplemented));
}
#endregion Implicit
class Program
{
static void Main(string[] args)
{
II AEasII = new AExplicit();
II BEIndependentasII = new BExplicitIndependent();
II BEasII = new BExplicitReImplemented();
AExplicit BEIndependentasAE = new BExplicitIndependent();
BExplicitIndependent BExplicitIndependentAsItself = new BExplicitIndependent();
AEasII.f(); // IL: callvirt instance void xxx.II::f()
BEIndependentasII.f(); // IL: callvirt instance void xxx.II::f()
BEasII.f(); // IL: callvirt instance void xxx.II::f()
// BEIndependentasAE.f(); no such method in A
BExplicitIndependentAsItself.f(); // new method IL: callvirt instance void xxx.BExplicitIndependent::f()
II AIasII = new AImplicit();
II BIIndependentasII = new BImplicitIndependent();
II BIasII = new BImplicitReImplemented();
AImplicit BIIndependentAsAI = new BImplicitIndependent();
BImplicitIndependent BIIndependentAsItself = new BImplicitIndependent();
AIasII.f(); // IL: callvirt instance void xxx.II::f()
BIIndependentasII.f(); // IL: callvirt instance void xxx.II::f()
BIasII.f(); // IL: callvirt instance void xxx.II::f()
BIIndependentAsAI.f(); // A method, but it has nothing to do with interfaces // IL: callvirt instance void xxx.AImplicit::f()
BIIndependentAsItself.f(); // B method, but it also has nothing to do with interfaces // IL: callvirt instance void xxx.BImplicitIndependent::f()
}
}

据我了解,映射规则描述了如何从代码中选取方法实现,它没有描述如何在运行时选取任何内容。正在调用唯一的实现(针对任何特定调用(。如果需要,可以编译上面的代码(作为控制台应用程序并自行检查所有生成的 IL(。

最新更新