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
被调用。但是,据报道,我很困惑:

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

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

  • 对于
    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