首页 > [.NET] 《Effective C#》快速笔记 - C# 中的动态编程

[.NET] 《Effective C#》快速笔记 - C# 中的动态编程

《Effective C#》快速笔记 - C# 中的动态编程

 

  静态类型和动态类型各有所长,静态类型能够让编译器帮你找出更多的错误,因为编译器能够在编译时进行大部分的检查工作。C# 是一种静态类型的语言,不过它加入了动态类型的语言特性,可以更高效地解决问题。  

 

一、目录

  • 三十八、理解动态类型的优劣
  • 三十九、使用动态类型表达泛型类型参数的运行时类型
  • 四十、将接受匿名类型的参数声明为 dynamic
  • 四十一、用 DynamicObject 或 IDynamicMetaObjectProvider 实现数据驱动的动态类型
  • 四十二、如何使用表达式 API
  • 四十三、使用表达式将延迟绑定转换为预先绑定
  • 四十四、尽量减少在公有 API 中使用动态类型

 

三十八、理解动态类型的优劣

  1. C# 动态类型是为了让静态代码能够更加平滑地与其他使用动态类型的环境进行交互,而不是鼓励在一般场景中使用 dynamic 进行动态编程。

  2. 只要对象在运行时包含成员,那么即可正常使用。

  3. 若是一个操作数(包括 this)为动态类型,那么返回结果也会是动态类型。不过,最后依然要转换成静态类型,以便被其它 C# 代码所使用,以及被编译器感知。

  4. 当你需要不知道具体类型的运行时解析方法的时候,动态类型是最佳的工具。如果你能在编译期间明确类型,那么可以使用 lambda 表达式和函数式编程来解决问题。

  5. 表达式树,一种在运行时创建代码的方法(下面提供示例)。

  6. 大多数情况,可以使用 Lambda 表达式创建泛型 API,让调用者自己动态定义所需要执行的代码即可。

  7. 优先使用静态类型,静态类型比动态类型更高效,动态类型和在运行时创建表达式树都会带来性能上的影响,即便这点影响微不足道。

  8. 若你能控制程序中所有涉及的类型时,可以引入一个接口,而不是动态类型,即基于接口编程,并让所有需要支持该接口行为的类型都实现该接口。通过 C# 类型系统可以减少代码在运行时所产生的错误,编译器也能够生成更加高效的代码。

  9. 动态类型做法的效率比纯粹的静态类型下降挺大,但实现的难度却比解析表达式树要简单地挺多。

 

  这里,我使用 3 种数字相加的方法,dynamic 动态、Func 委托以及使用表达式树进行相加的区别:

        [TestMethod]public void Test(){var result1 = AddDynamic(2, 3);Console.WriteLine((int)result1);var result2 = AddFunc(3, 4, (x, y) => x + y);Console.WriteLine(result2);var result3 = AddExpressionTree(4, 5);Console.WriteLine(result3);}/// /// Add,动态/// /// /// /// private dynamic AddDynamic(dynamic a, dynamic b){return a + b;}/// /// Add,使用委托/// /// /// /// /// /// /// /// private TR AddFunc(T1 a, T2 b, Func func){return func(a, b);}/// /// Add,使用表达式树/// /// /// /// /// private T AddExpressionTree(T a, T b){ParameterExpression leftOperand = Expression.Parameter(typeof(T), "left");ParameterExpression rightOperand = Expression.Parameter(typeof(T), "right");BinaryExpression body = Expression.Add(leftOperand, rightOperand);Expression> adder = Expression.Lambda>(body, leftOperand, rightOperand);Func theDelegate = adder.Compile();return theDelegate(a, b);}}

 

三十九、使用动态类型表达泛型类型参数的运行时类型

  1. System.Linq.Enumerable.Cast 将序列中的对象转换成 T,从而使得 LINQ 可以配合 IEnumerable 进行工作。

  2. Convert 要比 Cast 适用性更广,但同时也会执行更多的工作。

 

四十、将接受匿名类型的参数声明为 dynamic

  1. 不要过度使用动态类型,因为动态调用会增加系统的额外开销,即便不大。

  2. 长远来看,具体类型更易于维护,编译器和类型系统也会为其提供更好的支持。

  3. 扩展方法不能基于动态对象定义。

 

四十一、用 DynamicObject 或 IDynamicMetaObjectProvider 实现数据驱动的动态类型

  1. 创建带有动态功能的类型的最简单的方法就是继承 System.Dynamic.DynamicObject。若能直接继承 DynamicObject,那么创建动态类就会比较简单。

  2. 实现 IDynamicMetaObjectProvider 就意味着需要实现方法 GetmetaObject()。

  3. 创建动态类型时首选继承,如果必须使用其他基类,可以手工实现 IDynamicMetaObjectProvider 接口,虽然所有的动态类型都会带来性能上的损失,但这种手工实现接口的方式所带来的损失往往更大一些。

 

  这是一个实现动态类型模型的一个示例。除了需要继承 DynamicObject,还需要重写 TryGetMemebr() 和 TrySetMemebr()。

        [TestMethod]public void Test1(){dynamic propDynamic = new DynamicPropertyBag();propDynamic.Now = DateTime.Now;Console.WriteLine(propDynamic.Now);}/// /// 动态属性绑定模型/// internal class DynamicPropertyBag : DynamicObject{private readonly Dictionary<string, object> _storage = new Dictionary<string, object>();/// /// 获取属性值/// /// /// /// public override bool TryGetMember(GetMemberBinder binder, out object result){var key = binder.Name;if (_storage.ContainsKey(key)){result = _storage[key];return true;}result = null;return false;}/// /// 设置属性值/// /// /// /// public override bool TrySetMember(SetMemberBinder binder, object value){var key = binder.Name;try{if (_storage.ContainsKey(key)){_storage[key] = value;}else{_storage.Add(key, value);}return true;}catch (Exception e){Console.WriteLine(e);return false;}}

 

 

四十二、如何使用表达式 API

  1. 传统的反射 API 可以用表达式和表达式树进行更好的替代,表达式可以直接编译为委托。

  2. 接口使我们可以得到一个更为清晰、也更具可维护性的系统,反射是一个很强大的晚期绑定机制(虽然效率有所降低),.NET 框架使用它来实现 Windows 控件和 Web 控件的数据绑定。

 

  这里提供了一个示例:

        [TestMethod]public void Test2(){var result = Call<int, string>((x) => (x * 2).ToString("D"));Console.WriteLine(result);}/// /// 调用/// /// /// /// /// private TResult Call(Expression> op){var exp = op.Body as MethodCallExpression;var result = default(TResult);if (exp == null){return result;}var methodName = exp.Method.Name;var parameters = exp.Arguments.Select(ProcessArgument);Console.WriteLine($"方法名 {methodName}");foreach (var parameter in parameters){Console.WriteLine("参数:");Console.WriteLine($"	{parameter.Item1}:{parameter.Item2}");}return result;}/// /// 处理参数/// /// /// private Tupleobject> ProcessArgument(Expression expression){object arg = default(object);LambdaExpression l = Expression.Lambda(Expression.Convert(expression, expression.Type));Type parmType = l.ReturnType;arg = l.Compile().DynamicInvoke();return Tuple.Create(parmType, arg);}

 

四十三、使用表达式将延迟绑定转换为预先绑定

  1. 延迟绑定API要使用符号(symbol)信息来实现,而预先编译好的 API 则无需这些信息,表达式 API 正是二者之间的桥梁。

  2. 延迟绑定常见于 Silverlight 和 WPF 中使用的属性通知接口,通过实现 INotifyPropertyChanged 和 INotifyPropertyChanging 接口来实现属性变更的预绑定。

 

四十四、尽量减少在公有 API 中使用动态类型

  1. 优先使用 C# 的静态类型,并尽可能地降低动态类型的作用范围。若是想一直使用动态特性,你应该直接选用一种动态语言,而非 C#。

  2. 若要在程序中使用动态特性,请尽量不要在公有接口中使用,这样会将动态类型限制在一个单独的对象(或类型)中。

 

本系列

  《Effective C#》快速笔记(一)- C# 语言习惯

  《Effective C#》快速笔记(二)- .NET 资源托管

  《Effective C#》快速笔记(三)- 使用 C# 表达设计

  《Effective C#》快速笔记(四) - 使用框架

  《Effective C#》快速笔记(五) - C# 中的动态编程

  《Effective C#》快速笔记(六) - C# 高效编程要点补充

 

 


【博主】反骨仔

【原文】http://www.cnblogs.com/liqingwen/p/6816100.html

【GitHub】https://github.com/liqingwen2015/XMind 可以下载 XMind

【参考】《Effective C#》

 【参考】http://blog.csdn.net/w174504744/article/details/50562109

转载于:https://www.cnblogs.com/liqingwen/p/6816100.html

更多相关:

  • Python 与 ABC 的一个重要区别在于其类型系统。ABC 采用静态类型,编译器会检查程序中的变量类型是否保持一致,如果不一致,程序就无法运行。并且,ABC与当时大多数静态语言不同,采用的是类型推导(和 Haskell 一样),而不是类型声明(比如 C 语言)。而 Python 采用动态类型,所有类型检查都是在程序运行过程中,而不...

  • python是计算机二级考试的科目之一,并没有级别的划分。其考试目标是测试考生掌握Python语言知识的程度和对Python语言的编程能力、调试能力和综合应用能力,在当下的应用中是十分重要的。 什么是python Python语言是一种解释运行、面向对象、扩展性强的程序设计语言,是大学生学习计算机编程能力、理解计算机解决问题的方法的...

  • 正在学C,书上老说空指针,或者说void指针,对于我这样的生手来说,理解非常容易造成混淆,因为void这个单词的意思也是空,到底空指针的意思是指指向地址为空的类型呢,还是指void类型的指针呢 (1)空指针所对应的是指指向的对象为空的指针。            不经发问,什么叫指向为空呢?要理解这点,必须理解如下几点(有点啰嗦,但...

  • 一、reponseType 1、什么是reponseType XMLHttpRequest.reponseType属性是一个枚举类型的属性,返回响应数据的类型,他允许我们手动的设置 返回数据的类型。如果我们将它设置为一个空字符串,它将默认的使用"text"类型。 当将reponseType设置为一个特定的类型的时候需要确保服务...

  • SNMP中,数据类型并不多。这里我们就讨论这些数据类型,而不关心这些数据类型在实际中是如何编码的。INTEGER一个变量虽然定义为整型,但也有多种形式。有些整型变量没有范围限制,有些整型变量定义为特定的数值(例如,IP的转发标志就只有允许转发时的或者不允许转发时的这两种),有些整型变量定义一个特定的范围(例如,UDP和TCP的端口号...

  • nan 是not a number ,inf是无穷大 numpy.nan_to_num(x): 使用0代替数组x中的nan元素,使用有限的数字代替inf元素...

  • 简介 Simple Reference  基础CUDA示例,适用于初学者, 反映了运用CUDA和CUDA runtime APIs的一些基本概念.Utilities Reference  演示如何查询设备能力和衡量GPU/CPU 带宽的实例程序。Graphics Reference  图形化示例展现的是 CUDA, OpenGL,...

  • 在做开发的过程中难免需要给内核及下载的一些源码打补丁,所以我们先学习下Linux下使用如如何使用diff制作补丁以及如何使用patch打补丁。...

  • 我在调研ATS 4.2.3挂载SSD的过程中,遇到很多坑,特此详细记录我摸索的主要过程,以便大家以后避免之。 基本思路可以完全照搬参考文献[2][3] 下面的安装假定是以root用户身份进行的,Linux服务器已经安装好系统,磁盘已经做好分区。 首先需要认识我们的Linux服务器的硬件配置和软件情况 硬件配置: DELL...

  • 该博文整理一些在使用stl编程过程中遇到的小经验: 1.在多线程环境下面打印调试,如何使用cout及时刷新到屏幕上? 在C中我们经常这样使用: printf("Hello World "); fflush(stdout); 如果使用stl,我们可以这样使用: cout << "Hello World" << endl <...