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
被标记为类型AAble
的IAble.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
(如果它没有实现)。