代码之家  ›  专栏  ›  技术社区  ›  Narek

C#调用接口方法非虚拟实现

  •  4
  • Narek  · 技术社区  · 6 年前

    我是C语言的新手,我不明白为什么编译器不抱怨这段代码。以下是类的层次结构:

    interface IAble
    {
        void f();
    }
    
    class AAble : IAble
    {
        public void f()
        {
            Debug.Log("---->> A - Able");
        }
    }
    
    class BAble : AAble
    {
        public void f()
        {
            Debug.Log("---->> B - Able");
        }
    }
    

            IAble i = new BAble();
            i.f();
    

    执行时 ---->> A - Able 是印刷的。为什么?编译器如何知道应该调用什么函数?

    什么时候决定调用什么函数-运行时还是编译时? 如果我玷污了一个新班级呢 class CAble : IAble ?

    4 回复  |  直到 6 年前
        1
  •  2
  •   ikkentim    6 年前

    因为 AAble 正在实施 IAble 接口,its AAble.f 被标记为 IAble.f 类型的方法 A表

    BAble.f 只是隐藏 表f 方法,它不会重写它。

    IAble o = new BAble(); o.f(); // calls AAble.f
    AAble o = new BAble(); o.f(); // calls AAble.f
    BAble o = new BAble(); o.f(); // calls BAble.f
    IAble o = new CAble(); o.f(); // calls CAble.f
    

    在编译时作出决定:

    // AAble.f in IL:
    .method public final hidebysig newslot virtual 
        instance void f () cil managed 
    
    // BAble.f in IL:
    .method public hidebysig 
        instance void f () cil managed
    

    接口实现标记为 virtual 在IL中,即使它在C#中没有标记为virtual。方法也标记为 final 事实上的 在C#中,它不会被标记为 最终的 .

        2
  •  1
  •   Prodigle    6 年前

    通常会有一个编译器警告,因为它隐藏了一个方法。但在C#中,对非虚函数这样做是合法的。当然,如果它是一个虚拟函数,那么很明显,该方法的B版本将运行。

    因为您将它声明为IAble并且它是非虚拟的,所以编译器将它读取为IAble。如果它被声明为virtual,编译器将扫描继承的层次结构,并将看到它的实际类是一个BAble,它将运行BAble代码。

        3
  •  1
  •   Fabjan    6 年前

    class AAble : IAble
    {
        public void f() { ... }
    }
    
    class BAble : AAble
    {
        // An attempt to explicitly implement interface in BAble through AAble class
        void IAble.f()
        {
            Console.WriteLine("---->> B - Able");
        }
    }
    

    当我们向上抛的时候 BAble 到接口 IAble 来自的实现 AAble 因为它是compile prospective中唯一实现接口的类。

    我们可以直接从接口继承,这将告诉编译器应该使用哪个接口实现:

    class BAble : AAble, IAble
    {
        // Now it compiles
        void IAble.f()
        {
            Console.WriteLine("---->> B - Able");
        }
    }
    

    输出: ---->> B - Able"

    或者我们可以使用多态性。这将告诉编译器 使用重写函数:

    class AAble : IAble
    {
        public virtual void f()
        {
            Debug.Log("---->> A - Able");
        }
    }
    
    class BAble : AAble, IAble
    {
        public override void f()
        {
            Console.WriteLine("---->> B - Able");
        }
    }
    
        4
  •  1
  •   Michał Turczyn    6 年前

    当您在派生类中定义与基类中具有相同签名的方法时,您是 躲藏 是的。

    CAble 用法如下:

    BAble c = new CAble();
    b.f();
    

    那么结果就是 ---->> B - Able .

    IAble . 它没有实现,所以它看实现,它是在类中定义的 AAble . 其他类只隐藏方法。

    new keywrods,显式隐藏方法(这将表明隐藏是有意的)。

    你所期望的是 覆盖 override keywaord,定义方法时。

    virtual (如有实施)或 abstract (如果它没有实现)在基类中。

    推荐文章