动态语言运行时 (DLR) 是一种新运行时环境,它将一组适用于动态语言的服务添加到 CLR。借助于 DLR,可以更轻松地开发要在 .NET Framework 上运行的动态语言,而且向静态类型化语言添加动态功能也会更容易。为了支持 DLR,在 .NET Framework 中添加了新 System.Dynamic 命名空间。
动态语言可以在运行时标识对象的类型,而在类似 C# 和 Visual Basic 的静态类型化语言中(当您使用 Option Explicit On 时),您必须在设计时指定对象类型。动态语言有:Lisp、Smalltalk、JavaScript、PHP、Ruby、Python、ColdFusion、Lua、Cobra 和 Groovy。
大多数动态语言都会向开发人员提供以下优点:
可以使用快速反馈循环(REPL 或读取-计算-打印循环)。这样,您就可以在输入几条语句之后立即执行它们以查看结果。
同时支持自上而下的开发和更传统的自下而上的开发。例如,当您使用自上而下的方法时,可以调用尚未实现的函数,然后在需要时添加基础实现。
更易于进行重构和代码修改操作,原因是您不必在代码中四处更改静态类型声明。
利用动态语言可以生成优秀的脚本语言。利用新的命令和功能,客户可以轻松地扩展使用动态语言创建的应用程序。动态语言还经常用于创建网站和测试工具、维护服务器场、开发各种实用工具以及执行数据转换。
DLR 的目的是允许动态语言系统在 .NET Framework 上运行,并为动态语言提供 .NET 互操作性。在 Visual Studio 2010 中,DLR 将动态对象引入到 C# 和 Visual Basic 中,以便这些语言能够支持动态行为,并且可以与动态语言进行互操作。
DLR 还可帮助您创建支持动态操作的库。例如,如果您具有一个使用 XML 或 JavaScript 对象表示法 (JSON) 对象的库,则对于使用 DLR 的语言,您的对象可以显示为动态对象。这使库用户能够编写语法更简单且更自然的代码,以便操作对象和访问对象成员。
例如,在 C# 中,您可能会使用下面的代码来递增 XML 中的计数器值。
Scriptobj.SetProperty("Count", ((int)GetProperty("Count")) + 1);
通过使用 DLR,您可以改用下面的代码来执行相同的操作。
scriptobj.Count += 1;
与 CLR 类似,DLR 是 .NET Framework 的一部分,并随 .NET Framework 和 Visual Studio 安装包一起提供。DLR 的开放源代码版本还可以从 CodePlex 网站下载获得。
说明
DLR 的开放源代码版本具有 Visual Studio 和 .NET Framework 中包含的 DLR 的所有功能。此版本还提供对语言实现的其他支持。有关更多信息,请参见 CodePlex 网站上的相关文档。
使用 DLR 开发的语言的示例包括:
DLR 的主要优点
简化动态语言到 .NET Framework 的移植
借助于 DLR,语言实施者不必再按传统的方式来创建词法分析器、语法分析器、语义分析器、代码生成器以及其他工具。若要使用 DLR,语言需要生成表达式树(以树形结构表示语言级代码)、运行时帮助器例程以及用于实现 IDynamicMetaObjectProvider 接口的可选动态对象。DLR 和 .NET Framework 可以自动执行许多代码分析和代码生成任务。这样,语言实施者就可以将精力集中在独有的语言功能上。
允许在静态类型化语言中使用动态功能
现有的 .NET Framework 语言(如 C# 和 Visual Basic)可以创建动态对象,并将动态对象与静态类型化对象一起使用。例如,C# 和 Visual Basic 可以将动态对象用于 HTML、文档对象模型 (DOM) 和 .NET 反射。
提供 DLR 和 .NET Framework 的未来好处
通过使用 DLR 实现的语言可以从将来的 DLR 和 .NET Framework 改进中获益。例如,如果 .NET Framework 发布的新版本改进了垃圾回收器或加快了程序集加载时间,则通过使用 DLR 实现的语言会立即获得相同的好处。如果 DLR 优化了某些方面(如编译功能得到改进),则通过使用 DLR 实现的所有语言的性能也会随之提高。
允许共享库和对象
使用一种语言实现的对象和库可供其他语言使用。DLR 还允许在静态类型化语言和动态语言之间进行互操作。例如,C# 可以声明一个动态对象,而此对象使用利用动态语言编写的库。同时,动态语言也可以使用 .NET Framework 中的库。
提供快速的动态调度和调用
DLR 通过支持高级多态缓存,能够快速执行动态操作。DLR 首先会创建一些规则以将使用对象的操作绑定到必需的运行时实现,然后缓存这些规则,以避免在对同一类型的对象连续执行相同代码期间,出现将耗尽资源的绑定计算。
public enum StringSearchOption //自定义动态对象使用一个枚举来确定搜索条件
{
StartsWith,
Contains,
EndsWith
}
using System.IO;
using System.Dynamic;
class DynamicXMLNode : DynamicObject
{
XElement node;
public DynamicXMLNode(XElement node)
{
this.node = node;
}
public DynamicXMLNode()
{
}
public DynamicXMLNode(String name)
{
node = new XElement(name);
}
public override bool TrySetMember( SetMemberBinder binder, object value)
{
XElement setNode = node.Element(binder.Name);
if (setNode != null)
setNode.SetValue(value);
else
{
if (value.GetType() == typeof(DynamicXMLNode))
node.Add(new XElement(binder.Name));
else
node.Add(new XElement(binder.Name, value));
}
return true;
}
重写 DynamicObject 类的 TryGetMember 方法。当请求动态类的成员且未指定任何参数时,将调用 TryGetMember 方法。binder 参数包含有关被引用成员的信息,而 result 参数则引用为指定的成员返回的结果。TryGetMember 方法会返回一个布尔值,如果请求的成员存在,则返回的布尔值为 true,否则返回的布尔值为 false。
public override bool TryGetMember( GetMemberBinder binder, out object result)
{
XElement getNode = node.Element(binder.Name);
if (getNode != null)
{
result = new DynamicXMLNode(getNode);
return true;
}
else
{
result = null;
return false;
}
}
public override bool TryConvert( ConvertBinder binder, out object result)
{
if (binder.Type == typeof(String))
{
result = node.Value;
return true;
}
else
{
result = null;
return false;
}
}
重写 DynamicObject 类的 TryInvokeMember 方法。当使用参数请求动态类的成员时,将调用 TryInvokeMember 方法。binder 参数包含有关被引用成员的信息,而 result 参数则引用为指定的成员返回的结果。args 参数包含一个传递给成员的参数的数组。TryInvokeMember 方法会返回一个布尔值,如果请求的成员存在,则返回的布尔值为 true,否则返回的布尔值为 false。
public override bool
使用方法:.
dynamic contact = new DynamicXMLNode("Contacts");contact.Name = "Patrick Hines";contact.Phone = "206-555-0144";contact.Address = new DynamicXMLNode();contact.Address.Street = "123 Main St";contact.Address.City = "Mercer Island";contact.Address.State = "WA";contact.Address.Postal = "68402";TryInvokeMember(
InvokeMemberBinder binder,
object[] args,
out object result)
{
Type xmlType = typeof( XElement);
try
{
result = xmlType.InvokeMember(
binder.Name,
BindingFlags.InvokeMethod |
BindingFlags.Public |
BindingFlags.Instance,
null, node, args);
return true;
}
catch
{
result = null;
return false;
}
}
}