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

我是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(); if(); 

执行---->> A - Able被打印出来。 为什么? 编译器如何知道应该调用哪个函数?

当决定调用什么函数 – 运行时或编译时? 如果我玷污一个新的class CAble : IAble怎么class CAble : IAble

由于AAble正在实现IAble接口,因此其AAble.f被标记为类型AAbleIAble.f方法的实现。

BAble.f只是隐藏了AAble.f方法,它没有覆盖它。

 IAble o = new BAble(); of(); // calls AAble.f AAble o = new BAble(); of(); // calls AAble.f BAble o = new BAble(); of(); // calls BAble.f IAble o = new CAble(); of(); // 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#中,非虚拟function是合法的。 但是,当然,如果它是虚函数,那么显然该方法的B版本将运行。

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

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

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

当我们将IAble上传到接口IAble使用来自IAble的实现,因为它是实现接口的编译预期中唯一的类。

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

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

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

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

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

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

 BAble c = new CAble(); bf(); 

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

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

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

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

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