gpt4 book ai didi

c# - C# 中的非虚拟方法、静态绑定(bind)和接口(interface)

转载 作者:行者123 更新时间:2023-11-30 20:08:17 27 4
gpt4 key购买 nike

我知道非虚拟方法是静态绑定(bind)的,这意味着,据我所知,它在编译时本身就知道将在哪个对象上调用哪个方法。该决定是根据对象的static 类型做出的。让我感到困惑的是接口(interface)(而不是)和静态绑定(bind)。

考虑这段代码,

public interface IA
{
void f();
}
public class A : IA
{
public void f() { Console.WriteLine("A.f()"); }
}
public class B : A
{
public new void f() { Console.WriteLine("B.f()"); }
}

B b = new B();
b.f(); //calls B.f() //Line 1

IA ia = b as IA;
ia.f(); //calls A.f() //Line 2

演示代码:http://ideone.com/JOVmi

我理解第 1 行。编译器可以知道 b.f() 将调用 B.f() 因为它知道 bstatic 类型是 B

但是编译器如何在编译时自己决定 ia.f() 将调用A.f()?对象 iastatic 类型是什么?不是IA吗?但那是一个接口(interface),没有任何f()的定义。那它是怎么工作的呢?

为了让这个案例更加令人费解,让我们考虑一下这个static 方法:

static void g(IA ia)
{
ia.f(); //What will it call? There can be too many classes implementing IA!
}

正如评论所说,实现接口(interface)IA的类可能太多,那么编译静态如何决定使用哪个方法ia.f( ) 会打电话吗?我的意思是,假设我有一个类定义为:

public class C : A, IA 
{
public new void f() { Console.WriteLine("C.f()"); }
}

如您所见,CB 不同,它除了从 A 派生之外还实现了 IA。这意味着,我们在这里有不同的行为:

g(new B()); //inside g(): ia.f() calls A.f() as before!
g(new C()); //inside g(): ia.f() doesn't calls A.f(), rather it calls C.f()

演示代码:http://ideone.com/awCor

我如何理解所有这些变体,尤其是接口(interface)和静态绑定(bind)如何协同工作?

还有一些(ideone):

C c = new C();
c.f(); //calls C.f()

IA ia = c as IA;
ia.f(); //calls C.f()

A a = c as A;
a.f(); //doesn't call C.f() - instead calls A.f()

IA iaa = a as IA;
iaa.f(); //calls C.f() - not A.f()

请帮助我理解所有这些,以及 C# 编译器如何完成静态绑定(bind)。

最佳答案

But how does the compiler decide at compile-time itself that ia.f() will call A.f()?

事实并非如此。它知道 ia.f() 将在 ia 中包含的对象实例上调用 IA.f()。它发出此调用操作码,并让运行时在执行调用时找出它。

这是将为示例代码的下半部分发出的 IL:

    .locals init (
class B V_0,
class IA V_1)
IL_0000: newobj instance void class B::'.ctor'()
IL_0005: stloc.0
IL_0006: ldloc.0
IL_0007: callvirt instance void class B::f()
IL_000c: ldloc.0
IL_000d: stloc.1
IL_000e: ldloc.1
IL_000f: callvirt instance void class IA::f()
IL_0014: ret

请注意,callvirt 用于两种情况。之所以使用它,是因为运行时能够自行确定目标方法何时为非虚拟方法。 (此外,callvirtthis 参数执行隐式 null 检查,而 call 则不会。)

这个 IL 转储应该可以回答您的所有其他问题。简而言之:编译器甚至不会尝试解析 final方法调用。这是运行时的工作。

关于c# - C# 中的非虚拟方法、静态绑定(bind)和接口(interface),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7216437/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com