代码改变世界

Effective C# 学习笔记(四十)使用Dynamic处理匿名类型参数

2011-07-30 20:52  小郝(Kaibo Hao)  阅读(872)  评论(0编辑  收藏  举报

有时你需要在你的方法的参数或返回值中使用匿名类型,而编译器不允许你这么使用匿名类型。你可以使用范型方法,或使用System.Object类型做参数来处理这种问题。但有时你又需要一些特定类型的行为特性来完成你的逻辑,而这个特定类型又不完全被你所掌握,这时你就需要使用Dynamic来处理这个问题。

 

举个例子:

你有一个逻辑是打印所有货物的物价清单,但是货物的存货信息、特殊订单信息以及第三方供货信息分别存储在三个不同的系统中。你可以通过API访问三个系统获取这些信息,但由于每个系统的货物的基类和接口及货物的nameprice属性名称也不尽相同。这时你可能会想到用到adapter pattern(适配器模式),但是这样的工作量是蛮大的。虽然这种模式使用静态类型,可以拥有很高的执行性能,但是对于新增加的货物类型的处理又得进行新的封装。

 

这时你可以选择使用Dynamic来处理这种利用匿名类型来解决类型统一的问题。见如下代码:

public static void WritePricingInformation(dynamic product)

{

Console.WriteLine("The price of one {0} is {1}", product.Name, product.Price);

}

 

上面的代码使用Dynamic关键字作为参数类型,声明货物为匿名类型。方法体中就可以随意调用货物的属性了。然后就是同一你不同系统中不同类型的属性名称。如下代码所示:

var price = from n in Inventory

where n.Cost > 20

select new { n.Name, Price = n.Cost * 1.15M };//创建匿名类型对象,供WritePricingInformation方法使用

 

对于那些已经拥有namePrice属性的类型来说你可以直接使用,如下代码所示:

public class DiscountProduct

{

public static int NumberInInventory { get; set; }

public double Price { get; set; }//注意:这里的price使用了double类型,而上面的投影使用的是decimal类型,这没有问题,dynamic会在运行时处理好这些不同

public string Name { get; set; }

public string ReasonForDiscount { get; set; }

// other methods elided

}

 

注意:虽然dynamic带来了匿名类型参数方法方便,但是这样的转换会相对于静态类型更加耗费资源和运行时间,所以在可以使用静态类型的时候尽量使用静态类型。此外,你可以通过添加静态类型参数方法重载,来提供部分的程序运行性能,因为编译器会在运行时首先使用静态类型参数方法。如下代码所示:

public class Product

{

public decimal Cost { get; set; }

public string Name { get; set; }

public decimal Price

{

get { return Cost * 1.15M; }

}

}

// Derived Product class:

public class SpecialProduct : Product

{

public string ReasonOnSpecial { get; set; }

// other methods elided

}

// elsewhere

public static void WritePricingInformation(dynamic product)

{

Console.WriteLine("The price of one {0} is {1}", product.Name, product.Price);

}

public static void WritePricingInformation(Product product)

{

Console.WriteLine("In type safe version");

Console.WriteLine("The price of one {0} is {1}",product.Name, product.Price);

}