您需要记住的是,动态分辨率基本上与静态分辨率执行相同的过程,但在运行时执行。任何无法由clr解决的问题都不会由dlr解决。
让我们来看看这个小程序,它是受您的启发而设计的,完全不使用动态:
namespace ConsoleApplication38 {
public interface IActualInterface {
void Store(object entity);
}
public interface IExtendedInterface : IActualInterface {
}
public class TestInterface : IExtendedInterface {
public void Store(object entity) {
}
}
public abstract class ActualClass {
public abstract void Store(object entity);
}
public abstract class ExtendedClass : ActualClass {
}
public class TestClass : ExtendedClass {
public override void Store(object entity) {
}
}
class Program {
static void TestInterfaces() {
IActualInterface actualTest = new TestInterface();
IExtendedInterface extendedTest = new TestInterface();
TestInterface directTest = new TestInterface();
actualTest.Store(null);
extendedTest.Store(null);
directTest.Store(null);
}
static void TestClasses() {
ActualClass actualTest = new TestClass();
ExtendedClass extendedTest = new TestClass();
TestClass directTest = new TestClass();
actualTest.Store(null);
extendedTest.Store(null);
directTest.Store(null);
}
static void Main(string[] args) {
TestInterfaces();
TestClasses();
}
}
}
一切都很好。但是编译器真正生成了什么呢?让我们看看使用ildasm。
对于接口:
// actualTest.Store
IL_0015: callvirt instance void ConsoleApplication38.IActualInterface::Store(object)
// extendedTest.Store
IL_001d: callvirt instance void ConsoleApplication38.IActualInterface::Store(object)
// directTest.Store
IL_0025: callvirt instance void ConsoleApplication38.TestInterface::Store(object)
我们可以看到,C编译器总是为定义方法的接口或类生成调用。
IActualInterface
有存储方法槽,因此用于
actualTest.Store
.
IExtendedInterface
不,所以
IActualInterface(IActualInterface)
用于呼叫。
TestInterface
使用定义新方法存储
newslot
IL修饰符,有效地为该方法在vtable中分配了一个新的槽,因此它直接用于
directTest
属于类型
测试接口
.
对于课程:
// actualTest.Store
IL_0015: callvirt instance void ConsoleApplication38.ActualClass::Store(object)
// extendedTest.Store
IL_001d: callvirt instance void ConsoleApplication38.ActualClass::Store(object)
// directTest.Store
IL_0025: callvirt instance void ConsoleApplication38.ActualClass::Store(object)
对于3种不同的类型,会生成相同的调用,因为方法槽是在ActualClass上定义的。
现在让我们看看,如果我们自己编写IL,使用我们想要的类型,而不是让C编译器为我们选择它,我们会得到什么。我已将IL修改为如下所示:
接口:
// actualTest.Store
IL_0015: callvirt instance void ConsoleApplication38.IActualInterface::Store(object)
// extendedTest.Store
IL_001d: callvirt instance void ConsoleApplication38.IExtendedInterface::Store(object)
// directTest.Store
IL_0025: callvirt instance void ConsoleApplication38.TestInterface::Store(object)
上课:
// actualTest.Store
IL_0015: callvirt instance void ConsoleApplication38.ActualClass::Store(object)
// extendedTest.Store
IL_001d: callvirt instance void ConsoleApplication38.ExtendedClass::Store(object)
// directTest.Store
IL_0025: callvirt instance void ConsoleApplication38.TestClass::Store(object)
这个程序可以用ILASM编译。但是,它未能通过peverify并在运行时崩溃,错误如下:
未处理的异常:
System.MissingMethodException:方法
未找到:'无效
consoleapplication38.iextendInterface.store(system.object)'.
在
控制台应用程序38.program.testinterfaces()。
在
consoleapplication38.program.main(字符串[]
ARGS)
如果删除此无效调用,派生类调用将正常工作,不会出现任何错误。CLR能够从派生类型调用解析基方法。但是,接口在运行时没有真正的表示,并且clr无法从扩展接口解析方法调用。
理论上,C编译器可以直接发出对运行时指定的正确类的调用。它可以避免出现关于中产阶级电话的问题。
Eric Lippert's blog
. 然而,正如所演示的,这对于接口来说是不可能的。
让我们回到DLR。它以与clr完全相同的方式解析方法。我们已经看到了
IExtendedInterface.Store
无法由clr解析。DLR也不能!C编译器将发出正确的调用这一事实完全隐藏了这一点,因此在使用
dynamic
除非你完全知道它在CLR中是如何工作的。