知识需要不断积累, 总结和沉淀, 思考和写作是成长的催化剂
内容目录
一, 概述二, 反射 1, 反射使用 2, 创建对象 3, 调用方法 4, 字段属性三, 特性四, 总结
一, 概述
反射其实无处不在, 我们用 VS 进行调试时候, 查看成员列表, 修改变量值都是通过反射来实现的. 我们写业务代码可能很少去写反射, 但理解反射是从菜鸟到大牛的必经之路. 无论 EF 还是 ASP.NET, 几乎所有框架都用到反射. 反射动态创建对象, 动态赋值, 动态调用方法.
前面简单介绍过. NET 的第一次编译, 会编译成 IL(中间语言), 反射就是利用 IL 在运行时获取类的各种信息(字段, 方法, 构造函数等), 并且可以动态的创建对象调用方法. 反射就是通过使用 metadata 的过程.
每一个类对应有一个 Type 对象, 方法对应一个 MethodInfo 对象, 属性对应一个 PropertyInfo, 这些都是一个类的元数据 (MetaData) 保存在 IL 中, 所以解析 IL 可以获取一个类的各种信息.
特性就和反射绑定的, 没有反射, 特性就无从使用. 特性本质就是给类, 方法等元素添加一些额外的信息和行为. 特性添加编译后也产生 IL, 我们没法直接使用的, 只在 MetaData 中有记录, 我们只能用过反射得到.
二, 反射
1, 反射使用
使用 System.Reflection..Net 框架提供帮助类库来进行反射. 通常像下面这样使用, 加载绝对 / 相对路径下的 dll.(注意加载某一个 dll, 它内部依赖于别的类库, 需要把它们都放在统一路径下)
Type 的获取, 可以从对象获取, 类名获取或类全路径获取 (通过配置项实例化类很方便).Activator.CreateInstance(type) 创建类实例. 加载 dll 创建某个实例用的都只是字符串, 既然是字符串那么就可以放在配置中. 像下面这种如果要换个国家的人来打招呼就需要替换红色字符即可.(前提当然是每个国家的人都继实现 ISayHello 打招呼接口). 依赖于具体类型改为依赖字符配置文件了.
最常用的还是数据库访问层的封装, 不同数据库的访问都实现 IDBHelper 接口, 如果换不同类型的数据库, 就只需要修改配置文件即可.
2, 创建对象
上面通过反射仅调用无参数构造函数, 那么有参数的呢. 像下面这样, 给个 object[]数组指定参数.
另外需要注意反射创建对象可以调用私有的构造函数, 这意味着它可以对单例模式造成破坏.(单例模式在设计模式中会详细了解, 就是一个类全局只有一个实例). 具体看下面实例对比.
还有一种泛型类型的反射创建比较特殊. 假设有以下泛型类, 那么在 GetType 时候需要使用 "`"(反单引号)占位符, 这个符号键盘上一般在 ESC 键下方或数字 1 键的左边. 后接数字表示需要多少个泛型类型, 然后指定具体的类型通过 MakeGenericType 再次创建出 Type.
后面在容器里会看到, IoC 类型创建都是利用反射通过注解扫描或配置.
3, 调用方法
像下面注释说明的那样, 包括类的私有方法也可以反射调用. 泛型方法和泛型类的创建类似, 需要在获取指定名称泛型方法基础上, 用类型数组代替泛型方法类型参数.
方法也可以通过字符指定, 虽然调用起来比较麻烦, 但的确很灵活. 像 MVC 框架里 URL 的映射就是利用类名 + 方法名来调用后台的.
4, 字段属性
字段和属性的获取也类似. 通过 GetFields,GetProperties 获取.
三, 特性
Attribute 特性标签, 也被叫做注解, 用来给我们的类, 属性, 方法等附加一些元信息, 这些信息一般不影响我们代码的实际逻辑, 起辅助作用, 给框架或编译器去解析的. 通过 Type 对象仍然可以轻松获取注解对象. 表格中的内置注解我们不陌生
特性 | 说明 |
---|---|
[Obsolete] | 表明此成员已过时 |
[ReadOnly(true)] | 在编辑器中只读,代码赋值不受影响 |
[DisplayName("姓名")] | 属性的显示名 |
[Browsable(false)] | 属性是否可见 |
[Serializable] | 序列化和反序列化 |
除了. NET 框架内置的特性, 我们通过继承 Attribute 可以自定义一个特性. 例子中 AttributeUsage 是专用于标注自定义特性的特性, 可以指定该特性的作用目标是类, 方法还是字段等, AllowMultiple 允许同一个元素上可以多重标记该特性.
使用特性时, 像下面这样. 在需要标记的元素上用总括号包裹特性类. 语法类似调用构造函数, 先是构造函数的参数(有参, 无参), 然后可以命名赋值属性.
编码时标记的特性, 不去调用, 只直观的看, 是没有什么意思的. 通过反编译工具 ILSpy 查看 IL 代码, 会发现在每个加了特性的元素 (类, 属性, 方法等) 处会生成特性类型的构造函数. 既然在 IL 中存在构造函数, 那我们就可以通过反射直接创建类实例.
我们会有一个整体感觉, 特性就是给类, 属性, 方法等元素添加一个额外的, 补充的描述, 而没有破坏原有类的封装. 像实体类属性上的数据的规范性检查特性一样, 我们可以定制统一的属性检查特性类, 然后编码中只需要在需要检查的属性上加上特性标记即可. 通过反射就可以轻松获取这些补充信息, 大概像下面这样, 我们对 object 类型增加 Validate 扩展方法, 这样所有的类型都可以调用这个扩展方法, 然后通过反射查找类型内部是否标记有需要验证的属性.
类似还有枚举的别名描述, 字典项指等. 获取某一枚举的中文别名描述信息或实体类中某一个字段和数据库表对应的列名(不一致时).
四, 总结
反射意味着动态. 面向对象开发, 对象之间协同完成某一功能, 本身应该算耦合的, 修改一个, 其他依赖都需要修改, 反射就可以把依赖抽象, 依赖于扩展, 运行时动态构建对象去完成某一功能, 使其更加灵活, 常见的插件开发和 IoC 容器就是典型应用. 但反射也有缺点, 就是写起来复杂, 而且因为是动态的, 避开了编译器的检查, 性能也肯定不如硬编码, 这种性能我们大可不用太担心, 因为影响程序性能占比最大的应该还是我们本身的编码设计能力.
特性是和反射绑定的..NET 框架内置了很多特性, 我们可能用不到自己手写特性, 但明白其中的原理是必要的, 在 Bs 开发中, wcf,mvc 等都用到特性注解. Java web 中 Spring MVC 也是通过注解来扫描装配 Bean 的. 特性和注释可是完全不同的, 注释就是给人看, 对程序一点影响都没有, 特性可以影响程序的运行.
如果手机在手边, 也可以关注下 vx:xishaobb, 互动或获取更多消息. 当然这里也一直更新 de.
来源: https://www.cnblogs.com/xibei/p/11699697.html