(3)C# 4动态编程技术内幕
C#4中所定义的dynamic变量可以引用以下类型的对象:
l 传统的“静态”的CLR对象。
l COM包装器对象。前面已经介绍了这方面的内容。
l 实现了IDynamicMetaObjectProvider接口的“动态对象”,ExpandoObject就是这种类型对象的实例。
l 基于DLR实现的动态语言(比如IronRuby和IronPython)所创建的对象。
从C#程序员角度来看,所有这四种对象都是一样的,都可用一个dynamic变量引用之,而DLR在程序运行时动态地将方法调用和字段存取请求“绑定”到真正的对象上。
dynamic的功能是由DLR所支撑的,是C#编译器与DLR分工合作的成果。
请看以下示例代码:
dynamic d = 100; d++; |
C#编译器在处理上述代码时,它并不去检查变量d是否可以支持自增操作,而是为其创建了一个CallSite<T>对象(<>p__Site1):
private static class <Main>o__SiteContainer0 { public static CallSite<Func<CallSite, object, object>> <>p__Site1; } |
中文MSDN将CallSite<T>译为“动态(调用)站点”,它是DLR中的核心组件之一。
动态站点对象通过CallSite<T>.Create()方法创建, C#编译器会为其指定一个派生自CallSiteBinder的对象(称为“动态站点绑定对象”)作为其参数。
动态站点绑定对象是与具体语言相关的,比如IronPython和C#都有各自的动态站点绑定对象。
动态站点绑定对象的主要工作是将代码中的动态表达式(本例中为d++)转换为一棵“抽象语法树(AST:Abstract Syntax Tree)”,这棵语法树被称为“DLR Tree”,是在.NET 3.5所引入的LINQ表达式树的基础上扩充而来的,因此,有时又称其为“表达式树(Expression Tree)”
DLR在内部调用此表达式树的Compile()方法生成IL指令,得到一个可以被CLR所执行的委托(在本例中其类型就是Func<CallSite, object, object>)。
动态调用站点对象(本例中为<>p__Site1)有一个Target属性,它负责引用这一生成好的委托。
委托生成之后,动态表达式的执行就体现为委托的执行,其实参由C#编译器直接“写死”在IL代码中。
简化的代码示意如下(通过Reflector得到,为便于阅读,修改了变量名):
object d = 100; object CS$0$0000 = d; if (<>p__Site1 == null) <>p__Site1 = CallSite<Func<CallSite, object, object>>.Create(……); d = <>p__Site1.Target(<>p__Site1, CS$0$0000); |
上述类型推断、方法绑定及IL代码生成的工作都是在程序运行时完成的。