第六周的NuGet软件包介绍 – Clay中动态、可塑、有趣的Expando对象与Clay
[原文发表地址]: NuGet Package of the Week #6 - Dynamic, Malleable, Enjoyable Expando Objects with Clay
[原文发表时间]:May 6, 2011, 4:41 AM
嘿,您实施了NuGet行动计划吗?行动起来吧,它只需5分钟——升级到1.2版本,安装自动更新,获取NuGet 软件包浏览器。 NuGet1.3已经out了,所以请确保您已经设置了自动更新!
故事背景:我正在思考既然NuGet.Net软件包管理网站开始逐渐充实,那么我应该开始在那寻找宝石了(没有双关含义)。您知道,就是那种大家可能没法发现的真正有用的东西。我将主要寻找开放源代码项目——我认为它们很有用。我会查看它们如何生成NuGet软件包,看看它们设计盒外(out of the box)安装体验时有没有什么有趣的地方(以及任何它们可以改进而实施的举措),当然也会关注软件包本身所做的事。
本周的推荐软件包是“Clay”。它使得用动态对象工作变得更有趣。它是Louis DeJardin和助手Bertrand LeRoy为开放源代码Orchard项目而编写的。
静态语言中有趣的动态
这是从我2年前的一篇关于C#动态关键字的文章中复制/粘贴来的一些内容。我认为内容不错,所以又把它添加到这里了。
所以我问这个人,动态关键字到底是什么,它到底是什么类型呢?我的意思是,C#不是动态的,对吗?他回答:
“哦,它是静态被赋予了动态类型。”
一瞬间我的大脑爆炸并溢出耳外。真的,尽管只有一秒钟的时间。这是Anders幻灯片里的一些很好的例子:
Calculator calc = GetCalculator();
int sum = calc.Add(10, 20);
这就是创建对象,方法调用以及返回值集合。下面是完全相同的代码,因为”var”类型在编译时被推算出来。
var calc = GetCalculator();
int sum = calc.Add(10, 20);
如果您想做同样的事,除了Reflection以外(比如如果它是其他类,可能是old-COM互操作,或者编译器不知道Add()的先验是否可行,等等)您可以这样做:
object calc = GetCalculator();
Type calcType = calc.GetType();
object res = calcType.InvokeMember("Add",
BindingFlags.InvokeMethod, null,
new object[] { 10, 20 });
int sum = Convert.ToInt32(res);
当然,它看起来很可怕。如果对象是动态的,我们可以这么做:
dynamic calc = GetCalculator();
int sum = calc.Add(10, 20);
然后获得动态方法调用和返回类型转换。基本上,看上去就像我们在调用其它任何对象一样。
我和我的好友``Rob Coner``y``都热爱动态语言,但我们也热爱
.NET CLR。如果我们以自己的方式行事,就会给动态语言运行库和Iron.NET 语言更多支持。我们主要使用ASP.NET网页编写https://thisdeveloperslife.com 网站,因为这个网站主要使用动态的Razor模板引擎,而在代码中我们使用了动态类型。
有些人认为静态语言与动态语言没有任何关系,但我并不同意这一观点。正如他们所说的,成功编译只是第一次的单元测试,我喜欢能在静态与动态中做出选择。
Expando和动态
在.NET中,Expando对象是一个动态类型,您可以动态地为它添加或移除成员。它适合处理动态数据。您可以这样做:
dynamic myObject = new ExpandoObject();
myObject.WhateverMakesMeHappy = "Scott";
突然间,我有了一个新属性。您甚至可以将Expando“cast”成其他类型并按其他类型使用。这很疯狂。来操作吧。
通过对象初始化器生成匿名对象是个很好的选择,但一旦你生成一个对象,它就会以那种方式固定不变。例如以下从Bertrand Le Roy的博客中截取的内容。
Html.TextBoxFor(m => m.CurrentUser, new {
title = "User Name",
style = "float:left;"
})
看到对象初始化器了吗?它生成了匿名对象,但是它将永远保持那种标题和样式。
为什么需要Clay?
引用Bertrand的话:
就API可用性而言, Expando对象并不创新,特别是它并不能帮助你生成深层动态对象图。其行为固定且不能扩展。
另一方面来说, Clay具有高度的可延伸性并侧重于深层图表的创建和使用。**
Clay有一个灵巧的命名规则(虽然你可能不喜欢它。放松,这只是个规则),在这种规则下您将ClayFactory实例命名为”New”。对的,首字母N大写-“New”。大脑又一次爆炸了。
当然,您可以通过Expando来实现的一般操作,也可以使用Clay来完成。但是,取决于具体情况的不同,您可以使用不同的技术,这也是有趣的地方所在。下面是 Bertrand 和 Lou提供的一些例子,以创建ClayFactory开头:
dynamic New = new ClayFactory();
现在这个“New”对象会帮助我们创建新的Clay对象,正如其名称所示(尽管这个名称只是一个规则)。然后:
var person = New.Person();
person.FirstName = "Louis";
person.LastName = "Dejardin";
比如在Clay中,索引器语法和属性访问器是等效的,就像它们在JavaScript中一样。
当你需要你通过名字访问某个属性,但是在编译时您又不知道名称的情况下,这是非常有用的:
var person = New.Person();
person["FirstName"] = "Louis";
person["LastName"] = "Dejardin";
您还可以将属性当成可链接setter函数来用,风格同jQuery:
var person = New.Person()
.FirstName("Louis")
.LastName("Dejardin");
或者您可以传递一个匿名对象,此对象将成为一个Clay对象:
var person = New.Person(new {
FirstName = "Louis",
LastName = "Dejardin"
});
更好的是,Clay也理解命名参数,这样我们就可以写出如下内容:
var person = New.Person(
FirstName: "Louis",
LastName: "Dejardin"
);
或像这样的数组:
var people = New.Array(
New.Person().FirstName("Louis").LastName("Dejardin"),
New.Person().FirstName("Bertrand").LastName("Le Roy")
);
这些都意味着以下内容都是等效的:
var people = New.Array(
New.Person().FirstName("Louis").LastName("Dejardin"),
New.Person().FirstName("Bertrand").LastName("Le Roy")
);
要开始使用Clay,我推荐您安装Clay.Sample,而不用NuGet安装Clay软件包。这是开源项目的一个惯例,包含一个实例包,而这个实例包对项目本身有依赖关系。这样在安装完实例后,您就可以同时获得这两个包。
这里有一些其他不错的例子,它们能真正让您了解该如何像粘土(Clay)一样在动态和静态环境中切换:
public interface IPerson {
string FirstName { get; set; }
string LastName { get; set; }
}
public static void CastToCLRInterface() {
dynamic New = new ClayFactory();
var person = New.Person();
person.FirstName = "Louis";
person.LastName = "Dejardin";
// Concrete interface implementation gets magically created!
IPerson lou = person;
// You get intellisense and compile time check here
Console.WriteLine("{0} {1}", lou.FirstName, lou.LastName);
}
我想看到那些掌管大权的人,比如*咳*Anders*咳*签出像Clay的软件包这样的东西,并将它内置在语言里。这可就太好了。