之前我提出了一个问题,但没有得到充分回答,因此我决定重新制定我的问题,以了解正在发生的事情:
下面是我的类层次结构:
interface I
{
void f();
}
class A : I
{
// non virtual method
public void f()
{
Debug.Log("---->> A ");
}
}
class B : A
{
// non overriding but hiding class A method
public void f()
{
Debug.Log("---->> B ");
}
}
class C : I
{
// non virtual method
public void f()
{
Debug.Log("---->> C ");
}
}
以下是执行代码:
Random rnd = new Random();
var randomI = rnd.Next(0, 2);
I i = null;
if (randomI == 0)
{
i = new B();
}
else
{
i = new C();
}
i.f();
就像现在一样,它将输出 A 或 C。它不会输出 B。
这里有一个问题:你能解释一下如何通过这些步骤来决定调用什么函数吗?
IMHO,有趣的部分是为什么:((I)newB())。f()
打印
---->> A
将< code>B转换为< code>I,将使用基类方法。如果您想打印<代码>-
if (i is B)
((B)i).f();
else
i.f();
当强制转换为< code>I时,这就是类声明的情况:
I -> A -> B
|_ f() is implemented in subclasses, let's go one step down;
I -> A -> B
|_ f() is found, let's call A's f();
如果你想让 cast 调用 B 的实现,让 B 直接实现 I:
class B : A, I
因此,当向我施法时,会发生以下情况:
// Paths from I to B
I -> A -> B
I -> B // Shorter path, let's go via this one.
I -> B
|_ f() is implemented in subclasses, let's go one step down;
I -> B
|_ f() is found, let's call B's f();
当然,这是真实情况的一个简单版本,但是它有助于理解这个概念
当决定调用什么函数时——运行时还是编译时?
在编译时,如果有人将< code>B强制转换为< code>I并对其调用< code>f,编译器会确定< code>A.f是要调用的方法。在运行时,如果涉及到< code>B的实例(比如说< code>C),它就会调用该方法。换句话说,关键的决定是在编译时做出的。
请注意,如果该方法是虚拟
的,请参阅@YeldarKurmangaliyev的答案,了解它如何调用“继承链中的顶级方法”(但这不是这里的场景)。
如何决定调用什么函数的机制是什么?
规范的相关部分是13.4.5接口实现继承:
类继承其基类提供的所有接口实现。如果不显式地重新实现接口,派生类就不能以任何方式更改它从基类继承的接口映射。
这就是为什么类 B:
A 显示 A 但类 B:A,I 显示 B
。由于使用后者,您正在显式重新实现接口。
规范中的示例(这基本上是您的场景):
类继承其基类提供的所有接口实现。如果不显式重新实现接口,派生类不能以任何方式更改它从其基类继承的接口映射。例如,在声明中
interface IControl
{
void Paint();
}
class Control: IControl
{
public void Paint() {...}
}
class TextBox: Control
{
new public void Paint() {...}
}
TextBox中的Paint方法隐藏Control中的Paint方式,但它不会改变Control的映射。在IControl上涂漆。Paint,以及通过类实例和接口实例调用Paint将具有以下效果
Control c = new Control();
TextBox t = new TextBox();
IControl ic = c;
IControl it = t;
c.Paint(); // invokes Control.Paint();
t.Paint(); // invokes TextBox.Paint();
ic.Paint(); // invokes Control.Paint();
it.Paint(); // invokes Control.Paint();
该规范还谈到了使用虚拟
(这是比显式指定 B
实现 I
更常见的解决方案):
但是,当接口方法映射到类中的虚方法时,派生类有可能重写虚方法并改变接口的实现。
在编译时,它绑定对I
接口的调用,然后在运行时,它调用继承链中实现I. f()
的顶级方法。
所以,在你的代码中
A a = new A();
a.f();
B b = new B();
b.f();
将使编译器执行以下指令:
结果为“A”和“B”。
但是,当您这样做时:
I i;
i = new B();
i.f();
您使其编译以下说明:
在< code>i.f()行,它不知道< code>new B()被赋给了< code>i,它可能是从其他地方传递过来的。它只知道有一些实现< code>I的抽象对象实例,它需要调用它的< code>f()方法。
您可以考虑新
方法,例如具有不同名称的方法:
public class B : A
{
// non overriding but hiding class A method
public void anotherName()
{
Debug.Log("---->> B ");
}
}
A a = new A();
a.f();
B b = new B();
b.anotherName();
I i = new B();
i.f(); // this will obviously call the `A.f()` because there is no such method in B
唯一的区别是不能为继承的类实例调用隐藏方法。
问题内容: 该功能运行什么?它只会运行吗? 问题答案: setState()将按以下顺序运行函数: 如果您的组件正在接收道具,它将使用上述功能运行该功能。
基本问题:程序何时在C中调用类的析构函数方法?有人告诉我,每当对象超出范围或受到时都会调用它 更具体的问题: 1)如果对象是通过指针创建的,并且该指针后来被删除或给定一个新的地址来指向,它所指向的对象是否调用其析构函数(假设没有其他东西指向它)? 2) 接下来是问题1,什么定义了对象何时超出范围(与对象何时离开给定的{block}无关)。换句话说,什么时候对链表中的对象调用析构函数? 3) 你想手
问题内容: 我有以下功能 这段代码: 我的问题可能有点难以理解,所以请忍受:是什么使该代码段与常规调用完全区分开,或者是什么使该代码段需要引用函数变量而不是常规调用?() 我怎么知道应该在哪里引用该函数,以及什么时候该真正调用它? 问题答案: 好吧,该属性期望对函数的引用,以便在单击元素时执行该函数。通常是: 要么 (但是,当然,最好使用和) 请注意,它们都是如何引用函数而不是调用。 当某些东西需
问题内容: 代码很简单: 您会看到这里有一个函数,我们只在体内调用它一次。但是在控制台中,它打印两次: 您可以在此处观看现场演示:http : //plnkr.co/edit/tb8RpnBJZaJ73V73QISC?p=preview 为什么该函数已被调用两次? 问题答案: 在AngularJS中,用大括号括起来的任何东西都是一个在摘要循环 中至少 被求值 一次 的表达式。 __ Angular
我有jQuery脚本,它发送ajax请求到servlet。它的工作原理是,它将文本数据正确地发布到servlet,但是然后它调用错误函数,而不是成功函数(我检查过了,servlet发送回ajax而不是空字符串)。 为什么ajax方法调用错误函数? 这是剧本的代码 下面是servlet的代码 在错误报告中,textStatus值为,errorThrown值为void。
本文向大家介绍什么是JavaScript中的自调用匿名函数?,包括了什么是JavaScript中的自调用匿名函数?的使用技巧和注意事项,需要的朋友参考一下 在JavaScript中,用括号括起来的函数称为“立即调用函数表达式”或“自执行函数”。 包装的目的是为了命名空间并控制成员函数的可见性。它将代码包装在函数范围内,并减少了与其他库的冲突。这就是我们所说的立即调用函数表达式(IIFE)或自执行匿