关于DLR及.NET版本的问题

2010-07-26 21:12 by 老赵, 133 visits

动态语言运行时(Dynamic Language Runtime,DLR)是一套基于.NET的类库,它的作用是简化在CLR上开发动态语言的工作,例如DLR中提供了表达式树的创建,代码生成、优化及调试等实现动态语言的常见功能,而语言的编写者则着重关注解析器等方面的工作。不过最近接触了观察了DLR代码之后,却发现它和各版本.NET中BCL的协作还真是件不太容易理清的事情。

这还要从.NET 2.0说起,因为DLR的支持范围是.NET 2.0 SP1及.NET 4.0。.NET 2.0的类库较少,因此DLR要做的事情最多,例如它定义了一套完整的表达式树。不过到了.NET 3.5时,在新增的System.Core.dll中却也同样出现了一套表达式树的类库(例如BinaryExpression等等),而这套类库也完整包含在DLR中,它们的区别在于一个在System.Linq.Expressions命名空间下,而另一个在Microsoft.Scripting.Ast命名空间下。那么,如果您基于.NET 3.5编写代码的时候,您是利用哪一套代码?

事实上,您只能使用定义在DLR中的那套类库,因为一旦使用了.NET 3.5 BCL中的类库之后,它就无法与DLR中的其他组件进行交互了——除非您修改DLR的代码,让它与BCL中包含的表达式树进行合作。然而这么做还不一定可行,因为我们无法保证这些类库交互中是否会牵涉到内部状态。因此,在基于.NET 3.5开发DLR应用程序的时候,我们可以完全忽视BCL中表达式树的存在。

下图是DLR源代码中Codeplex-DLR.sln文件打开后的结果,其中Microsoft.Scripting.Core项目中的Ast文件夹便包含了一套“貌似”完整表达式树(抽象语法树):

不过到了.NET 4.0之后,状况又有所改变。您打开DLR源码中的Codeplex-DLR-Dev10.sln文件,便会发现Microsoft.Scripting.Core项目消失了,取而代之的是System.Core项目:

System.Core不是BCL中的类库吗?怎么会出现在DLR中?没错,这里的System.Core项目纯粹是来打酱油的,没有其他任何项目对它产生依赖。事实上,这个System.Core项目,也就是之前的Microsoft.Scripting.Core项目里的内容,已经完全融入在.NET 4的BCL里面了,它们分布在System.Core.dll的以下几个命名空间中:

  • Ast目录:System.Linq.Expressions命名空间
  • Actions目录:System.Dynamic命名空间
  • Compiler目录:System.Linq.Expressions.Compiler命名空间
  • Utils目录:System.Dynamic.Utils、System.Runtime.CompilerServices甚至System命名空间

如果您进一步阅读其中的内容,会发现代码在需要的情况下则使用了“条件编译符号”来切换其所在的命名空间,例如:

#if CLR2
namespace Microsoft.Scripting.Ast {
#else
namespace System.Linq.Expressions {
#endif

    /// <summary>
    /// Represents an expression that has a binary operator.
    /// </summary>
    public class BinaryExpression : Expression {
        ...

这样,在面向CLR 2(即.NET 2和.NET 3.5)和CLR 4的不同编译条件下,整套表达式树类库所在的命名空间是不同的。此外,如一些System.Dynamic命名空间下的类库,它们会使用条件编译符号依赖不同的表达式树类库,这也是显而易见的,例如:

#if CLR2
using Microsoft.Scripting.Ast;
using Microsoft.Scripting.Ast.Compiler;
using Microsoft.Scripting.Utils;
#else
using System.Linq.Expressions;
using System.Linq.Expressions.Compiler;
#endif

由此可见,.NET 4之前“声称”自己包含了DLR,事实上它只是包含了DLR的子集,也就是原本在Microsoft.Scripting.Core项目中的内容。那么假设我们要基于.NET 4和DLR开发一门新的语言,又该依赖哪些DLR组件呢?IronJS给了我们一个参考,您可以下载它的tag为0.1代码(目前的主代码无法编译通过),打开后便会发现它只是引用了Microsoft.Dynamic.dll和Microsoft.Scripting.dll两个程序集,其他的部分则已经包含在System.Core.dll中了:

如果我们要将其移植到.NET 3.5上,则需要引用一个额外的Microsoft.Scripting.Core.dll。我将IronJS移植到了.NET 3.5上并执行成功,项目引用情况如下:

基于我们之前的分析,其实您会发现,理想情况下将一个项目从.NET 4.0移植到.NET 3.5上并不需要修改太多代码,只要将所有对System.Linq.Expressions下类型的依赖修改至Microsoft.Scripting.Ast即可。当然在移植IronJS的过程中我还遇到了一些其他类库和语言方面的问题,例如一些使用了协变的代码,还有Enum类型中新增的HasFlag方法——幸好它没有使用C# 4.0中的dynamic,这些简单的问题只要兵来将挡,水来土掩就行了。

那么,Microsoft.Dynamic项目中究竟包含了什么呢?还是看看它的项目树吧:

您可能已经意识到了,这才是“真正”完整的DLR,包含了“真正”完整的表达式树类库及其他支持。我们有理由相信,所谓的Microsoft.Scripting.Core项目,其实只是为了.NET 4而特地分离出去的。从这点上看,DLR为了和.NET进行配合,真可谓是“用心良苦”。不过,我认为它们的做法及思路十分清晰,许多事情该怎么做,现在看来应该都是一目了然的。