运行时类型VS编译时类型的方法调用
的C#4.0规格如下:运行时类型VS编译时类型的方法调用
当调用虚拟方法,该实例的用于 运行时类型其中该调用发生确定实际方法 实施调用。在非虚拟方法调用中,实例的编译时类型是决定性因素。
起初,我认为这与初始化有关。例如,给定两个初始化:
BaseClass bcDerived = new Derived();
VS BaseClass bcBase = new BaseClass();
并在一个辅助类的过载:
public virtual void Method(Derived d)
{
Console.WriteLine("Result = derived called");
}
public virtual void Method(BaseClass d)
{
Console.WriteLine("Result = base called");
}
Method
invokation不是由在此情况下,virtual
关键字影响。无论标记为virtual
,调用的是派生最少的超载。只有在Derived类中的override
期间,方法调用才会更改。
那么,“运行时类型”和“编译时类型”是什么意思?它们如何影响方法调用?
这更多的是虚拟与非虚拟方法以及调用的发生方式。您引用的部分规格处理方法调用变量 - 调用bcDerived.SomeMethod()
,不调用foo.SomeMethod(bcDerived)
。
您正在引用的规范是指其中有非虚拟方法的情况下:
public class A
{
public void Foo() { Console.WriteLine("A.Foo"); }
public virtual void Bar() { Console.WriteLine("A.Bar"); }
}
public class B : A
{
public new void Foo() { Console.WriteLine("B.Foo"); }
public override void Bar() { Console.WriteLine("B.Bar"); }
}
然后调用将被确定由编译器,在编译时间的方法,这样做:
A someInst = new B();
someInst.Foo();
将导致此通过someInst
调用A.Foo()
,不管是什么子类A的被提及,因为这我这是一种非虚拟的方法。
但是,如果您有虚拟方法,则编译器将指定callvirt
指令,该指令会将决策移至运行时。这意味着:
someInst.Bar();
会打电话的B.Bar()
,不A.Bar()
。
就你而言,你并没有调用虚拟方法(在规范中指的是),而是在执行标准的方法解析。 C#Spec的7.5.3详细介绍了Overload解析。在你的情况下,参数列表(bcDerived
)由编译器检查,看起来被定义为类型BaseClass
。由于参数列表直接匹配参数列表,因此“最佳匹配”将为public virtual void Method(BaseClass d)
,因此在编译时使用。
如果您查看规范,则方法重载解析不会直接使虚方法调用生效 - 它只会查看类型之间的隐式转换。
在此实例中,参数的编译时间类型将始终用于确定调用哪个超载。虚拟调度依赖于调用方法的对象的运行时类型。
编译时类型是由编译器确定的对象类型,运行时类型是代码执行时的实际类型。要使用你的例子:
BaseClass bcDerived = new Derived()
的编译时类型是BaseClass
而运行时类型将是Derived
。
要了解我们需要稍微延长你的类的含义:
class BaseClass
{
public virtual void SomeMethod()
{
Console.WriteLine("In base class");
}
}
class Derived : BaseClass
{
public override void SomeMethod()
{
Console.WriteLine("In derived class");
}
}
现在呼吁bcDerived.SomeMethod()
将取决于运行时类型的bcDerived
至于BaseClass
实现是否会被调用或Derived
执行将被调用。
Eric Lippert在.Net(其中part one is here)中撰写了关于虚拟调度的很好的三部分系列,我强烈建议阅读它们以更充分地理解该主题。
Using these two classes as examples:
public class Parent
{
public void NonVirtual()
{
Console.WriteLine("Nonvirtual - Parent");
}
public virtual void Virtual()
{
Console.WriteLine("Virtual - Parent");
}
}
public class Child : Parent
{
public override void Virtual()
{
Console.WriteLine("Virtual - Child");
}
public void NonVirtual()
{
Console.WriteLine("Nonvirtual - Child");
}
}
虚拟和非虚拟之间的差被最清楚地通过此代码看出:
Parent childAsParent = new Child();
childAsParent.Virtual();
childAsParent.NonVirtual();
此打印:
Virtual - Child Nonvirtual - Parent
在它看到虚拟方法的情况下,在运行时,childAsParent
的类型是一个孩子,因此执行孩子的定义Virtual
。对于非虚拟方法,它看到该变量的编译时间类型为Parent
,并忽略实际实例为Child
并使用父级实现的事实。
virtual
不用于根据参数的类型确定使用哪种方法的过载。确定调用方法的哪个超载始终在编译时完成(不使用dynamic
时),从不在运行时,所以它总是会选择Method
的超载,在您的示例中,基于编译时间变量的类型。
请说明'Method'的声明以及使用方法。 –
@BrianRasmussen - 在OP中,这些方法是辅助类的成员。 –
谢谢。在这种情况下,我不确定接受的答案如何与您的问题相匹配,因为它不包含辅助类。但是,只要你对答案满意,我就很好。 –