我在观察外部DSL时,往往会关注它的语法到底提供了多少空间,例如一种XML的方言,利用XML方言的好处在于有不少现成的工具可用,这样可以更快地定义自己的语法。
而内部DSL,正像我之前说的那样,它其实只是一系列特别的API及使用模式的别称。这里则是一些LINQ查询语句,Ruby on Rails以及jQuery代码。内部DSL的特点是,它其实只是一系列API,但是你可以“假装”它们一种DSL。内部DSL往往会利用一些“流畅化”的技巧,例如像这里的LINQ或jQuery那样把一些方法通过“点”连接起来。有些则利用了元编程的方式,如这里的Ruby on Rails就涉及到了一些元编程。这种DSL可以访问语言中的代码或变量,以及利用如代码补全,重构等母语言的所有特性。
现在我会花几分钟时间演示一下我所创建的DSL,也就是LINQ。我相信你们也已经用过不少LINQ了,不过这里我还是快速的展示一下我所表达的更为“声明式”的编程方式。
public class Product
{
public int ProductID { get; set; }
public string ProductName { get; set; }
public string CategoryName { get; set; }
public int UnitPrice { get; set; }
public static List<Product> GetProducts() { /* ... */ }
}
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
List<Product> products = Product.GetProducts();
List<Product> result = new List<Product>();
foreach (Product p in products)
{
if (p.UnitPrice > 20) result.Add(p);
}
GridView1.DataSource = result;
GridView1.DataBind();
}
}
{
public int ProductID { get; set; }
public string ProductName { get; set; }
public string CategoryName { get; set; }
public int UnitPrice { get; set; }
public static List<Product> GetProducts() { /* ... */ }
}
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
List<Product> products = Product.GetProducts();
List<Product> result = new List<Product>();
foreach (Product p in products)
{
if (p.UnitPrice > 20) result.Add(p);
}
GridView1.DataSource = result;
GridView1.DataBind();
}
}
这里有许多Product对象,那么现在我要筛选出所有单价大于20的那些, 再把他们显示在一个GridView中。传统的做法就是这样,我先得到所有的Product对象,然后foreach遍历每个对象,再判断每个对象的单价,最终把数据绑定到GridView里。运行这个程序……(打开页面)这就是就能得到结果。