繁体   English   中英

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

[英]C# call an interface method non-virtual implementation

我是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怎么class CAble : IAble

因为AAble正在实现IAble接口,所以它的AAble.f被标记为类型AAbleIAble.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

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

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

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

这意味着当您使用基类型声明变量并使用dervied类型初始化它时,将使用基类中的方法。 这就是你的代码中的hapenning。

更一般:当你隐藏方法时,那么将使用的方法的版本,来自你声明它的类的cmoes。

所以,如果你有另一个类CAble并使用如下:

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

然后结果将是---->> B - Able

在您的情况下,您将IAble声明为IAble 它没有实现,因此它着眼于实现,它在类AAble定义。 其他类只隐藏方法。

为了隐藏方法,您可以使用相同的签名指定两个方法。 但是你应该总是使用new keywrods来明确隐藏方法(这表明隐藏是故意的)。

您期望的是重写方法,在定义方法时使用override keywaord完成。

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM