尽管C# 4.0中添加了 Dynamic特性,但是本质上说C#还是一个静态语言。而过多的使用动态特性会是你的程序难于维护,易于出错,所以我们要讲一个“度”字。
原则是:在必须使用Dynamic的时候使用之,但将使用Dynamic的逻辑的地方用静态类型的方式封装起来供外界调用。(如使用范型转换Dynamic类型)
如在《Effective C# 学习笔记(三十八)理解Dynamic的得与失》中我们说过的关于Dynamic的代码,其动态逻辑产生的代码是冗长的,而且在每次动态调用时都会动态生成,效率不高,如下代码所示:
dynamic answer = Add(5, 5);
Console.WriteLine(answer);
//其对应生成的C#代码如下:
// Compiler generated, not legal user C# code
object answer = Add(5, 5);
if (<Main>o__SiteContainer0.<>p__Site1 == null)
{
<Main>o__SiteContainer0.<>p__Site1 = CallSite<Action<CallSite, Type, object>>.Create(
new CSharpInvokeMemberBinder( CSharpCallFlags.None, "WriteLine",
typeof(Program), null, new CSharpArgumentInfo[]
{
new CSharpArgumentInfo(
CSharpArgumentInfoFlags.IsStaticType |
CSharpArgumentInfoFlags.UseCompileTimeType, null),
new CSharpArgumentInfo(
CSharpArgumentInfoFlags.None, null)
}));
}
<Main>o__SiteContainer0.<>p__Site1.Target.Invoke(<Main>o__SiteContainer0.<>p__Site1,
typeof(Console), answer);
我们可以这样来封装内部动态逻辑,而在外部只暴露静态逻辑。如下代码所示:
//私有动态加法逻辑
private static dynamic DynamicAdd(dynamic left, dynamic right)
{
return left + right;
}
// Wrap it:
public static T1 Add<T1, T2>(T1 left, T2 right)
{
//封装动态逻辑,返回由范型转换的动态类型对象
dynamic result = DynamicAdd(left, right);
return (T1)result;
}
上面的代码保证了动态逻辑的封装,对外只返回了静态类型的对象,而对于动态代码的生成也只会在静态方法Add中生成一次,既灵活地处理了动态的逻辑,又提高了效率,可谓一举两得。
而对于加法操作数和返回值类型都不同的方法,其可重载为以下代码:
public static TResult Add<T1, T2, TResult> (T1 left, T2 right)
{
dynamic result = DynamicAdd(left, right);
return (TResult)result;
}
// Type arguments needed because
// args are not the same type
answer2 = Add<int, double, double>(5, 12.3);
Console.WriteLine(answer);
再举一例,读取CSV文件中的数据。由于读取的每个CSV中的列不尽相同,所以需要考虑用Dynamic来实现其读取逻辑,代码如下:
public class CSVDataContainer
{
//CSV动态行类型
private class CSVRow : DynamicObject
{
//以二元组集合定义行对象
private List<Tuple<string, string>> values = new List<Tuple<string, string>>();
//构建行的方法
public CSVRow(IEnumerable<string> headers, IEnumerable<string> items)
{
values.AddRange(headers.Zip(items, (header, value) => Tuple.Create(header, value)));
}
//动态获取对应列的值得方法
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
var answer = values.FirstOrDefault(n => n.Item1 == binder.Name);
result = answer.Item2;
return result != null;
}
}
//列集合
private List<string> columnNames = new List<string>();
//行集合
private List<CSVRow> data = new List<CSVRow>();
//从流中读取CSV数据,并构建列和行
public CSVDataContainer(System.IO.TextReader stream)
{
using (stream)
{
var headers = stream.ReadLine();
columnNames = (from header in headers.Split(',')
select header.Trim()).ToList();
var line = stream.ReadLine();
while (line != null)
{
var items = line.Split(',');
data.Add(new CSVRow(columnNames, items));
line = stream.ReadLine();
}
}
}
//构建索引器
public dynamic this[int index]
{
get { return data[index]; }
}
//返回枚举行对象
public IEnumerable<dynamic> Rows
{
get { return data; }
}
}
//使用方法
StreamReader sr = new StreamReader(@"文件路径");
CSVDataContainer csvDataContainer = new CSVDataContainer(sr);
var rows = csvDataContainer.Rows;
foreach (var item in rows)
{
Console.WriteLine("{0}: {1}", item.Name, item.Company);
}