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

C# call an interface method non-virtual implementation

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

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。为什么?编译器如何知道应该调用什么函数?

当决定调用哪个函数时 - 运行时还是编译时? 如果我 defile 一个新的 class class CAble : IAble 怎么办?

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

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

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

这意味着当您使用基类型声明变量并使用派生类型对其进行初始化时,将使用基class 中的方法。这就是您的代码中发生的事情。

更一般:当您隐藏方法时,将使用的方法版本来自您声明它的class。

所以如果你有另一个 class CAble 并且像这样使用:

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

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

在您的例子中,您将变量声明为 IAble。它没有实现,因此它着眼于在 class AAble 中定义的实现。其他class只隐藏方法。

为了隐藏方法,您可以指定两个方法具有相同的签名。但是您应该始终使用 new 关键字来显式隐藏该方法(这将表明隐藏是故意的)。

你期望的是覆盖方法,在定义方法时使用override关键字完成。

为了覆盖方法,它应该在基础 class 中标记为 virtual(如果它有实现)或 abstract(如果它没有实现) .

接口必须在直接从它继承的 class 中实现,而不是在派生的 class 中实现。例如,这段代码将无法编译:

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 的实现,因为它是实现接口的编译预期中唯一的 class。

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

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");
    }
}

因为AAble正在实现IAble接口,它的AAble.f被标记为AAble类型的IAble.f方法的实现。

BAble.f 只是隐藏了 AAble.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

接口实现在 IL 中标记为 virtual,即使它在 C# 中未标记为虚拟。该方法在 IL 中也被标记为 final,如果该方法在 C# 中是 virtual,则它不会被标记为 final.