C# 为什么不是';t映射到派生类的非静态方法的接口成员
我编写了一个接口和一个实现它的基类C# 为什么不是';t映射到派生类的非静态方法的接口成员,c#,C#,我编写了一个接口和一个实现它的基类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"); } } cla
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
被调用。但是,据报道,我很困惑:
B
的实例,它与规则1不匹配,没有显式的接口成员实现。
但是,它有自己的非静态公共成员fn
,我认为这符合规则2。因此tan.fn()
应该调用B
的方法fn
,不需要在其基类A
中查找另一个fn
怎么了
(解决这个问题将帮助我理解接口重新实现),因为它是一个显式的接口实现。隐式实现接口也会使方法在B中可见 此外,为了能够在B中重写它,您需要将其设置为虚拟 规则2不适用于B,因为规则1适用。有一个显式接口实现,即从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");
}
}
class B : A, ITest
或者,如果确实希望显式实现接口,则可以让实现调用一个受保护的虚拟方法,您可以在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 A : ITest
{
public virtual void fn()
{
Console.WriteLine("fn in A");
}
}
class B : A
{
public override void fn()
{
Console.WriteLine("fn in B");
}
}
class B : A, ITest
然而,这有一个很大的缺点:该方法不像您通常期望的那样具有多态性。事实上,此代码将为您提供您期望的结果:
static void Main(string[] args)
{
ITest tan = new B();
tan.fn();
}
如果您的接口是在中显式实现的,那么下面的代码不会编译。但如果隐式实现,当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)。如果不重新实现接口,您可以重写恰好是接口一部分的方法。您可以在不使用虚拟方法的情况下重新实现接口:@Sergey.quixoticaxis.Ivanov。我更新了我的答案,提到了这个事实,但也指出了缺点。谢谢你,我提到它只是为了我所看到的正确性。重新实现是一种罕见的场景imho(我在生产代码中只使用过一次)。但是如果我将
B
的声明更改为class B:a,ITest
,它不会更改B
显式实现接口,即从a
继承的接口。所以规则1仍然适用,应该调用A
中的fn
?你是对的,实际上我认为接口是继承中一种特殊的基类型,这在这里没有意义。@Sergey.quixoticaxis.Ivanov