读书笔记—CLR via C#反射

前言

这本书这几年零零散散读过两三遍了,作为经典书籍,应该重复读反复读,既然我现在开始写博了,我也准备把以前觉得经典的好书重读细读一遍,并且将笔记整理到博客中,好记性不如烂笔头,同时也在写的过程中也可以加深自己理解的深度,当然同时也和技术社区的朋友们共享

程序集加载

  • AppDomain.Load
    • 尽量避免使用此方法加载程序集,因为Assembly不是从System.MarshalByRefObject派生,所以程序集对象必须按值封送回发出调用的那个AppDomain,但是CLR会使用发出调用的那个AppDomain的设置来定位并加载程序集。如果搜索策略和位置没有找到程序集,会抛出FileNotFoundException异常
  • Assembly.Load  推荐的程序集加载方式
  • Assembly.LoadFrom 支持从URI加载程序集
  • Assembly.LoadFile 加载程序集时,CLR不会自动解析任何依赖性问题;代码必须向AppDomain的AssemblyResolve事件登记,并让事件回调方式显式地加载任何依赖的程序集

反射的缺陷

  1. 编译时无法保证类型安全性。严重依赖字符串,丧失编译时的类型安全性
  2. 反射速度慢。加载IO并扫描元数据表,字符串搜索,字符串搜索执行的时不区分大小写的比较,这会进一步影响速度

Type类型

  • System.Type类型是执行类型和对象操作的起点。是从MemberInfo派生的抽象基类,具体派生类包括RuntimeType,ReflectionOnlyType,TypeDelegator,以及System.Reflection.Emit命名空间中的一些类型(包括EnumBuilder,GenericTypeParameterBuilder和TypeBuilder)
  • TypeDelegator允许代码封装一个Type对象,从而动态生成Type的一个子类,这样一来,就可以让原始Type负责处理大部分工作,同时重写一些需要自定义的功能。这个强大的机制允许重写反射的工作方式
  • 一个类型在AppDomain中被首次访问时,CLR会构造RuntimeType的一个实例,并初始化这个RuntimeType对象的字段以及Reflect关于类型的信息
  • 实际上Object的GetType非虚实例方法,晚期推断,返回的时RuntimeType对象的一个引用。RuntimeType是FLC内部定义的
  • 在一个AppDomain中,每个类型只有一个RuntimeType对象,所以可以使用相等和不等操作符进行判断
  • Type.GetType和重载版本也是一样返回RuntimeType对象引用
  • Type.ReflectionOnlyGetType,类型加载到一个“仅反射”的上下文中,不能执行
  • 实例方法GetNestedType和GetNestedTypes返回嵌套的子类型
  • MakeArrayType(int n) 创建n维数组
  • MakeByRefType方法: public void Test(ref Dock d); 通过反射获取方法参数类型 ParameterType == typeof(Dock).MakeByRefType()
  • Type的实例属性包括IsPublic,IsSealed,IsAbstract,IsClass和IsValueType,还有BaseType获得基类型等等
  • 动态构造类型
    • System.Activator.CreateInstance方法,还可以创建派生自MarshalByRefObject的System.Runtime.Remoting.ObjectHandle类型的远程对象,远程对象支持跨域传递,具体化时会调用ObjectHandle的Unwrap方法,如果对象按引用封送,会创建代理类型的对象。如果对象按值封送,对象的副本会被序列化。此方法还允许在不调用构造器的情况下创建值类型的一个实例
    • Activator.CreateInstanceFrom通过字符串指定类型及其程序集,程序集使用Assembly.LoadFrom加载到AppDomain中,由于不接受Type参数,返回的都是ObjectHandle对象引用,必须调用ObjectHandle的Unwrap方法进行具体化
    • AppDomain的方法,CreateInstance,CreateInstanceAndUnwrap,CreateInstanceFrom,CreateInstanceFromAndwrap
    • System.Type的InvokeMember实例方法,可使用一个Type对象引用来调用InvokeMember方法,查找与传递的实参匹配的一个构造器,并构造类型
    • System.Reflection.ConstructorInfo的Invoke实例方法
    • 数组、泛型、委托需要特殊的方式进行动态创建
      • 创建数组除了MakeArrayType,还有Array的静态CreateInstance方法。第一个参数都是对数组元素的Type的一个引用
      • 创建委托需要调用Delegate的静态CreateDelegate方法。第一个参数都是对想要创建的委托实例的Type的一个引用
      • 泛型类型的实例,需要使用MakeGenericType实例方法,将开放类型变成实际封闭类型
  • 注意DeclaringType和ReflectedType属性,一个是声明该成员的Type(类声明就是类,基类声明就是基类),一个是用于获取该成员的Type(返回MemberInfo所属于的那个类型,就是执行反射的类型)

类型成员

  • GetMembers获取所有成员
  • 返回特定成员就是GetNestedTypes, GetFields, GetConstructors, GetMethods, GetProperties, GetEvetns方法
  • 获取参数,调用GetParameters方法获取由ParametereInfo对象构成的一个数组
  • 查询只读属性ReturnParameter获得ParameterInfo对象获得成员返回值的详细信息
  • 泛型类型或方法,调用GetGenericArguments获得类型参数的集合
  • 对于任何一项都可以调用GetCustomAttributes方法获得应用于它们的自定义attribute的一个集合
  • BindingFlags, 筛选返回的成员种类
    • BindingFlags.FlattenHierarchy  返回基类型定义的静态成员
    • 如果指定Public或NonPublic,必须同时指定Instance|Static,否则将不返回成员
    • 调用指定成员的方法比如GetMethod或GetMethod,传递字符串,这是BindingFlags.IgnoreCase标志就有用了
  • 获取一个类型继承的接口集合
    • 调用Type类型的FindInterfaces(找到接口)
    • GetInterface或GetInterfaces方法(获取接口详细信息)
    • 返回代表接口的Type对象。注意,它扫描类型的继承层次结构,并返回在指定类型及其所有的基类型上定义的所有接口
    • 可能针对多个接口定义同一个方法,为了获得一个特定接口的MethodInfo对象,调用Type的GetInterfaceMap实例方法(传递接口类型作为实参)。返回System.Reflection.InterfaceMapping(一个值类型)的一个实例(包含了类型和方法的一些定义)
  • 对于字段或属性,可以获取值,对于构造函数或方法可以执行call获得返回值,PropertyInfo调用call属性的get和set访问其方法,EventInfo则可以通过Add或Remove方法添加或删除一个事件处理程序
  • Type类提供InvokeMember方法,可通过它调用一个成员
  • InvokeMember内部执行两个操作。首先,选择要调用的一个恰当的成员---绑定(bingding)其次,必须实际调用成员---调用(invoking)
  • Binder:从System.Reflection.Binder抽象类型派生的类型。提供BindToField, BindToMethod, ChangeType等方法,Microsoft定义了System.DefaultBinder内部使用
  • Type的所有的Get和Set系列的方法,在内部实际上都是调用InvokeMember,只是BindingFlags设置的区别
  • 性能的考量,一次绑定,多次调用。将MemberInfo保存起来
  • MemberInfo的实例方法都有一个重载版本要获取对Binder派生对象的引用以及一些BindingFlags,其实不会引起重绑定。调用这些方法时,要在Binder派生对象的帮助下,对提供的方法参数进行类型转换,BindingFlags唯一可以传递的标志时BindingFlags.SuppressChangeType。调用MemberInfo的Invokemethod来绑定并调用一个成员时,SuppressChangeType和ExactBinding可能都要指定,或者都不指定
  • Type和MemberInfo派生对象需要大量内存保存。对存储的性能也会有影响。替代方案就是使用FCL定义的句柄类型
  • 句柄类型:RuntimeTypeHandle, RuntimeFieldHandle, RuntimeMethodHandle。都是值类型,只包含一个IntPtr的字段,是一个句柄,引用了AppDomain的Loader堆中的一个类型、字段或方法。简单、高效的方式将重量级的Type或MemberInfo转换为轻量级的运行时句柄实例
  • 将Type转换为RuntimeTypeHandle,调用Type.GetTypeHandle,并传递那个Type对象引用,反过来转换,调用 Type.GetTypeFromHandle,并传递那个RuntimeTypeHandle. 其他的派生Memberinfo都有对于的方法转换为句柄

Tips

  • 生成程序集或模块时,编译器创建类型定义表、字段定义表、方法定义表等其他表,System.Reflection命名空间的类型为程序集或模块中包含的元数据创建了一个对象模型
  • 应用程序显式加载程序集,构造类型实例,再调用类型中定义的方法。以这种方式绑定并调用方法通常称为晚期绑定
  • 发现程序集中的类型 Assembly.GetExportedTypes
  • 晚期绑定使用 typeof(NeedType).IsAssignableFrom(t) 判断t对象是否兼容(实现或派生于)NeedType
  • 建议:为了获得较好的性能和编译时的类型安全,尽量避免使用反射。在动态可扩展应用程序下,构造好一个对象只恨,宿主代码一般要将对象转型为编译时已知的一个接口或者基类。这样一来,访问对象的成员时就可以获得较高的性能,而且可以确保编译时的类型安全性

后记

不得不说,这本书信息量真的是很大,以至于做笔记做完了都觉得没有好好整理和归纳,此笔记仅限于自己记录和使用,如果少许地方存在纰漏或勘误的情况,本人概不负责,最终还是要参考书本来学习和理解

时间: 2024-08-05 02:27:43

读书笔记—CLR via C#反射的相关文章

读书笔记—CLR via C#章节1-2

这本书这几年零零散散读过两三遍了,作为经典书籍,应该重复读反复读,既然我现在开始写博了,我也准备把以前觉得经典的好书重读细读一遍,并且将笔记整理到博客中,好记性不如烂笔头,同时也在写的过程中也可以加深自己理解的深度,当然同时也和技术社区的朋友们共享. 程序集 描述:一个或多个类型定义文件及资源文件的集合 特征:可重用.可保护.可版本控制的单元 生成:可通过C#编译器(或其他编译器)或AL.exe生成 组成: 托管模块(module) PE头,PE32或PE32+,面向CPU架构的信息 CLR头,

读书笔记—CLR via C#章节3

这本书这几年零零散散读过两三遍了,作为经典书籍,应该重复读反复读,既然我现在开始写博了,我也准备把以前觉得经典的好书重读细读一遍,并且将笔记整理到博客中,好记性不如烂笔头,同时也在写的过程中也可以加深自己理解的深度,当然同时也和技术社区的朋友们共享. 强命名程序集 优点 程序集共享 版本共存,解决DLL hell 安全策略(防篡改) 发布策略控制 标识组成 文件名(不计扩展名)+版本号+语言文化+公钥 CLR加载方式 弱命名程序集私有部署,CLR在基目录或子目录中搜索时只使用程序集名称 CLR搜

读书笔记—CLR via C#章节4-7

前言 这本书这几年零零散散读过两三遍了,作为经典书籍,应该重复读反复读,既然我现在开始写博了,我也准备把以前觉得经典的好书重读细读一遍,并且将笔记整理到博客中,好记性不如烂笔头,同时也在写的过程中也可以加深自己理解的深度,当然同时也和技术社区的朋友们共享 对象的祖先 – Object 公共方法 Equals, 虚方法,对象相等性,默认调用RuntimeHelpers.Equals方法 GetHashCode,虚方法,返回哈希吗(随机分布的整数),在哈希表中作为键使用 ToString , 默认返

读书笔记—CLR via C#异常和状态管理

前言 这本书这几年零零散散读过两三遍了,作为经典书籍,应该重复读反复读,既然我现在开始写博了,我也准备把以前觉得经典的好书重读细读一遍,并且将笔记整理到博客中,好记性不如烂笔头,同时也在写的过程中也可以加深自己理解的深度,当然同时也和技术社区的朋友们共享 Tips vs调试catch块时,监视窗口变量: $exception 查看当前抛出的异常对象 异常的catch是自上而下,回溯调用栈,如果未找到,就抛出未处理异常 异常的执行顺序:先执行body,再执行catch,最后执行finally 堆栈

读书笔记—CLR via C#同步构造28-29章节

前言 这本书这几年零零散散读过两三遍了,作为经典书籍,应该重复读反复读,既然我现在开始写博了,我也准备把以前觉得经典的好书重读细读一遍,并且将笔记整理到博客中,好记性不如烂笔头,同时也在写的过程中也可以加深自己理解的深度,当然同时也和技术社区的朋友们共享 类库和线程安全 在类设计中,类和方法的线程安全的设计尽量和FCL保持一致 保证所有的静态方法都是线程安全的 不保证实例方法是线程安全的 基元用户模式和基元内核模式构造 用户模式构造 易失构造(volatile construct),它包含一个简

读书笔记—CLR via C#章节11-13

前言 这本书这几年零零散散读过两三遍了,作为经典书籍,应该重复读反复读,既然我现在开始写博了,我也准备把以前觉得经典的好书重读细读一遍,并且将笔记整理到博客中,好记性不如烂笔头,同时也在写的过程中也可以加深自己理解的深度,当然同时也和技术社区的朋友们共享 事件 事件的本质 初始化为null的私有委托字段 封装add_Event和remove_Event方法 add_Event和remove_Event的可访问性同Event字段的可访问性,包括virtual和static修饰 事件的编码建议 使用

CLR via C#读书笔记 CLR寄宿和AppDomain

寄宿 寄宿是指让其他应用程序(非托管代码)使用CLR的能力,比如自己用C++开发的窗体能创建CLR实例. 托管代码也能调用非托管代码 [DllImport("kernel32.dll")] public static extern int WinExec(string exeName, int operType); 通常会调用win32 api,但是要查文档才知道怎么定义extern方法 CLR实际上被实现为COM服务器,可以通过CoCreateInstance或CLRCreateIn

读书笔记—CLR via C#线程25-26章节

前言 这本书这几年零零散散读过两三遍了,作为经典书籍,应该重复读反复读,既然我现在开始写博了,我也准备把以前觉得经典的好书重读细读一遍,并且将笔记整理到博客中,好记性不如烂笔头,同时也在写的过程中也可以加深自己理解的深度,当然同时也和技术社区的朋友们共享 线程 线程内部组成 线程内核对象 thread kernel object,在该结构中,包含一组对线程进行描述的属性.数据结构中还包括所谓的线程上下文thread context.上下文是一个内存块,包含了CPU的寄存器集合,占用几百到几千个字

读书笔记—CLR via C#委托和attribute

前言 这本书这几年零零散散读过两三遍了,作为经典书籍,应该重复读反复读,既然我现在开始写博了,我也准备把以前觉得经典的好书重读细读一遍,并且将笔记整理到博客中,好记性不如烂笔头,同时也在写的过程中也可以加深自己理解的深度,当然同时也和技术社区的朋友们共享 委托 类型安全的回调函数,函数签名定义声明.指向静态或实例方法 派生自System.MulticastDelegate的类 将方法绑定到委托时,C#和CLR都允许引用类型的协变性和逆变性 协变性指方法能返回从委托的返回类型派生的类型 逆变性是指