我是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
被打印。为什么?编译器如何知道应调用什么函数?
决定要调用的函数是运行时还是编译时?如果我de污新的类class CAble : IAble
怎么办?
答案 0 :(得分:2)
由于AAble
正在实现IAble
接口,因此其AAble.f
被标记为IAble.f
类型的AAble
方法的实现。
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
。
答案 1 :(得分:1)
通常情况下,会因为隐藏方法而出现编译器警告。但在C#中,对非虚拟功能执行合法操作。但是,当然,如果它是一个虚函数,则显然可以运行该方法的B版本。
由于您将其声明为IAble并且不是虚拟的,因此编译器将其读取为IAble。如果将其声明为虚拟的,则编译器将扫描继承的层次结构,并发现它的实际类是BAble,并且它将运行BAble代码。
答案 2 :(得分:1)
当您在派生类中定义与基类具有相同签名的方法时,您将隐藏。
这意味着当您声明具有基类型的变量并以dervied类型对其进行初始化时,将使用基类中的方法。这就是代码中的笨拙。
更笼统:当您隐藏方法时,然后是将要使用的方法的版本,即声明时使用的类的信息。
因此,如果您还有另一个类CAble
并按如下方式使用:
BAble c = new CAble();
b.f();
那么结果将是---->> B - Able
。
在您的情况下,您将可变参数声明为IAble
。它没有实现,因此它着眼于在类AAble
中定义的实现。其他类仅隐藏该方法。
为了隐藏方法,您可以指定两个具有相同签名的方法。但是,您应该始终使用new
键来显式隐藏该方法(这将表明该隐藏是有意的)。
您期望的是覆盖的方法,这些方法是在定义方法时通过使用override
keywaord来实现的。
为了重写方法,应在基类中将其标记为virtual
(如果有实现)或abstract
(如果没有实现)。
答案 3 :(得分:1)
接口必须在直接从其继承的类中实现,而不是在派生类之一中实现。例如,此代码将无法编译:
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");
}
}