你需要记住的是动态分辨率基本上和静态分辨率一样,但是在运行时。任何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
用于呼叫。 TestInterface
定义了一种新的方法Store,使用IL修改器,在该表的vtable中为该方法有效地分配了一个新插槽,因此directTest
的类型为TestInterface
,因此它可以直接使用。
对于类:
// 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和崩溃传:
Unhandled Exception: System.MissingMethodException: Method not found: 'Void ConsoleApplication38.IExtendedInterface.Store(System.Object)'. at ConsoleApplication38.Program.TestInterfaces() at ConsoleApplication38.Program.Main(String[] args)
如果删除该非法呼叫,派生类的调用没有任何错误做工精细。 CLR能够从派生类型调用中解析基本方法。但是,接口在运行时没有真实的表示,并且CLR无法从扩展接口解析方法调用。
理论上,C#编译器可以将调用直接发送到运行时指定的正确类。这样可以避免在Eric Lippert's blog上看到的有关中间电话的问题。然而如所证明的,这对于接口是不可能的。
让我们回到DLR。它解决了与CLR完全相同的方法。我们已经看到,CLR无法解决IExtendedInterface.Store
。 DLR也不能!这完全隐藏在C#编译器会发出正确调用的事实中,所以在使用dynamic
时一定要小心,除非您完全知道它在CLR中的工作原理。
发现这个工作在乌鸦不是吗? – 2011-08-12 18:55:43
@Chris确实没有! – 2011-08-22 19:10:08