Effective C# 学习笔记(四十四)合理地在C#中使用Dynamic特性

尽管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);

}

原文地址:https://www.cnblogs.com/haokaibo/p/2128985.html