1、反射的定义

反射(Reflection),是.Net中获取运行时类型信息的方式。程序集中有关程序及其类型的数据被称为元数据(metadata)。程序在运行时,可以查看其它程序集或其本身的元数据。一个运行的程序查看本身的元数据或者其他程序集的元数据的行为叫做反射。

2、反射应用场景

  • 获取有关加载的程序集和其中定义的类型的信息。
  • 在运行时创建、调用和访问类型实例。
  • 访问程序元数据的属性。
  • 检查和实例化程序集中的类型。
  • 在运行时构建新类型。
  • 执行后期绑定,访问在运行时创建的类型的方法。

3、反射的优缺点

优点:

  1. 可以实现动态创建对象和编译,能提高程序的灵活性和扩展性,降低耦合性。

缺点:

  1. 会模糊程序内部逻辑,代码变得更复杂。本来直接new一个对象的问题,反射需要更多的代码才能实现,而且代码不易读,维护成本高。
  2. 性能问题。反射的性能可能也是个老生常谈的问题了,但其实比非反射的性能差异并没有很巨大,特别是使用了缓存之后。

4、反射的使用

本节将详解如何加载程序集和获取其中定义的类型的信息以及如何在运行时创建、调用和访问类型实例。

4.1 使用Assembly加载程序集

4.1.1 Assembly提供三个方法来加载程序集:

    1. Load方法:通过程序集的长名称(包括程序集名,版本信息,语言文化,公钥标记)来加载程序集的,会加载此程序集引用的其他程序集。如没有找到程序集则会抛出异常。一般情况下应优先使用该方法。
    2. LoadFrom方法:从指定的路径来加载程序集,该方法底层还是调用Load方法进行加载,通过Load方法没找到时,不会抛出异常,而是会加载路径指定的程序集。该方式也会加载此程序集引用的其他程序集。
    3. LoadFile方法:从指定的文件来加载程序集。该方法不会调用Load方法,不会加载此程序集引用的其他程序集。

4.1.2 代码示例

         // 长格式名称加载 注意此处不需要后缀
Assembly assembly = Assembly.Load("MyReflection"); // 绝对路径加载
Assembly assembly1 = Assembly.LoadFrom(@"E:\project\Reflection\MyReflection\bin\Debug\MyReflection.dll");
// 如果要加载的程序集跟当前执行程序集处于同一路径,则可省略前面的路径
Assembly assembly2 = Assembly.LoadFrom(@"MyReflection.dll");
// 通过URL地址加载
Assembly assembly3 = Assembly.LoadFrom(@"http://www.test.com/MyReflection.dll"); // 必须传入绝对路径
Assembly assembly4 = Assembly.LoadFile(@"MyReflection.dll");

4.2 获取类型信息并动态创建、调用和访问类型实例

4.2.1 获取类型

    1. Assembly.GetTypes:获取程序集中所有的类型,返回类型数组。
    2. Assembly.GetType:通过类型全名获取程序集中的类,返回指定类的对象,没找到返回null。
    3. typeof():获取指定类的类型。
         // 获取所有类
Type[] types = assembly.GetTypes();
// 通过类名获取类
Type type2 = assembly.GetType("MyReflection.Chinese");
//
Type type3 = typeof(Chinese);

4.2.2 获取父类及接口信息

    1. Type.BaseType:返回父类信息。
    2. Type.GetInterfaces:获取实现或继承的所有接口。
    3. Type.GetInterface:根据名称获取实现或继承的接口,没找到返回null。
         Type type = typeof(Chinese);
// 获取父类
Type baseType = type.BaseType;
// 获取所有接口
Type[] interfaces = type.GetInterfaces();
// 通过接口全名获取接口
Type @interface = type.GetInterface("MyReflection.IInterface");

4.2.3 获取构造函数

    1. Type.GetConstructors:获取类型中所有构造函数。
    2. Type.GetConstructor:通过参数类型获取构造函数。
         Type type = typeof(Chinese);
// 获取所有构造函数
ConstructorInfo[] ctors = type.GetConstructors();
// 通过类型获取对应的构造函数
ConstructorInfo ctor = type.GetConstructor(new Type[] { typeof(string), typeof(int) });

4.2.4 创建类型的实例

    1. Activator.CreateInstance:创建类型的实例。默认调用无参数构造函数创建,可传入参数数组调用有参数构造函数。
         // 调用无参数构造函数
object obj = Activator.CreateInstance(type);
// 调用有参数构造函数 参数顺序、类型应与对应构造函数一致
object obj1 = Activator.CreateInstance(type, new object[] { "张三", });

4.2.5 获取/操作字段

    1. Type.GetFields:获取所有字段。
    2. Type.GetField:通过名称获取字段,未获取到返回null。
    3. FieldInfo.SetValue:给字段设置值。
    4. FieldInfo.GetValue:获取字段值。
         Type type = typeof(Chinese);
object obj = Activator.CreateInstance(type); // 获取所有的字段
FieldInfo[] fields = type.GetFields();
// 通过名称获取字段
FieldInfo field = type.GetField("Name"); // 设置值
field.SetValue(obj, "张三");
// 获取值
object fieldVal = field.GetValue(obj);

4.2.6 获取/操作属性

    1. Type.GetProperties:获取所有属性。
    2. Type.GetPropertie:通过名称获取属性,未获取到返回null。
    3. PropertyInfo.SetValue:设置值。
    4. PropertyInfo.GetValue:获取属性值。
         Type type = typeof(Chinese);
object obj = Activator.CreateInstance(type); // 获取所有的属性
PropertyInfo[] props = type.GetProperties();
// 通过名称获取属性
PropertyInfo prop = type.GetProperty("Name"); // 设置值
prop.SetValue(obj, "张三");
// 获取值
object propVal = prop.GetValue(obj);

4.2.7 获取/调用方法

    1. Type.GetMethods:获取所有方法。
    2. Type.GetMethod:通过名称获取方法,可传入参数类型来区分重载方法。
    3. MethodInfo.IsGenericMethod:是否是泛型方法。
    4. MethodInfo.ContainsGenericParameters:是否包含未赋值的泛型类型参数。
        Type type = typeof(Chinese);
object obj = Activator.CreateInstance(type); // 获取所有方法
MethodInfo[] methods = type.GetMethods(); // 获取并调用无参数方法
MethodInfo method = type.GetMethod("Eat");
method.Invoke(obj, null); // 获取并调用有参数方法
MethodInfo method1 = type.GetMethod("SayChinese");
method1.Invoke(obj, new object[] { "我是中国人" }); // 获取并调用无参数重载方法
MethodInfo method2 = type.GetMethod("SayHello", new Type[] { });
method2.Invoke(obj, null); // 获取并调用无参数重载方法
MethodInfo method3 = type.GetMethod("SayHello", new Type[] { typeof(string) });
method3.Invoke(obj, new object[] { "李四" }); // 获取并调用泛型方法
MethodInfo method4 = type.GetMethod("ShowObject");
// 判断方法是否是泛型方法
bool isGenericMethod = method4.IsGenericMethod;
// 判断泛型方法是否包含未赋值的泛型类型参数
bool containsGenericParameters = method4.ContainsGenericParameters;
// 替换泛型方法定义的类型参数
MethodInfo genericMethod = method4.MakeGenericMethod(new Type[] { typeof(int) });
// 调用
genericMethod.Invoke(obj, new object[] { });

4.2.8 获取事件及相关操作

    1. Type.GetEvents:获取声明或继承的所有事件。
    2. Type.GetEvent:通过事件名称获取事件。
    3. EventInfo.EventHandlerType:获取与此事件关联的基础事件处理程序委托的 Type 对象。
    4. Delegate.CreateDelegate:创建指定类型的委托。
    5. EventInfo.AddEventHandler:添加事件。
    6. EventInfo.RemoveEventHandler:移除事件。
         Type type = typeof(Chinese);
object obj = Activator.CreateInstance(type); // 获取声明或继承的所有事件
EventInfo[] events = type.GetEvents();
// 获取事件
EventInfo eventInfo = type.GetEvent("StudentEvent");
// 获取处理事件的委托类型
Type delegateType = eventInfo.EventHandlerType; // 查看委托签名
//Console.WriteLine(delegateType.GetMethod("Invoke").ToString()); // 获取要添加的方法
MethodInfo methodInfo = type.GetMethod("DelegateMethod", BindingFlags.Public | BindingFlags.Instance); // 创建委托
Delegate d = Delegate.CreateDelegate(delegateType, obj, methodInfo);
// 将委托实例添加到事件
eventInfo.AddEventHandler(obj, d); // 获取调用事件方法并调用
MethodInfo invokeMethod = type.GetMethod("InvokeEvent");
invokeMethod.Invoke(obj, null);

4.2.9 BindingFlags的使用

    1. BindingFlags:指定控制绑定和由反射执行的成员和类型搜索方法的标志,允许按位组合成员值。获取信息时,可传入该枚举进行过滤。

注:使用时必须包含BindingFlags.Instance和BindingFlags.Static中的一个,否则会找不到方法。

         // 获取所有的非公开的属性
PropertyInfo[] props = type.GetProperties(BindingFlags.NonPublic | BindingFlags.Instance);
// 获取所有公开的静态属性
PropertyInfo[] props1 = type.GetProperties(BindingFlags.Public | BindingFlags.Static);
// 在公开的实例方法中搜索
MethodInfo methodInfo = type.GetMethod("DelegateMethod", BindingFlags.Public | BindingFlags.Instance);

5、总结

本文列出了反射的一些基本用法,使用反射可以在运行时创建、调用和访问类型实例。

本节涉及源码可至gitHub进行下载。

C#基础系列-反射的更多相关文章

  1. C#基础系列——反射笔记

    前言:使用反射也有几年了,但是一直觉得,反这个概念很抽象,今天有时间就来总结下这个知识点. 1.为什么需要反射: 最初使用反射的时候,作为小菜总是不理解,既然可以通过new 一个对象的方式得到对象,然 ...

  2. C#基础系列 - 反射基础

    反射用于在程序运行过程中,获取类里面的信息或发现程序集并运行的一个过程.通过反射可以获得.dll和.exe后缀的程序集里面的信息.使用反射可以看到一个程序集内部的类,接口,字段,属性,方法,特性等信息 ...

  3. C#基础系列——Attribute特性使用

    前言:上篇 C#基础系列——反射笔记 总结了下反射得基础用法,这章我们来看看C#的另一个基础技术——特性. 1.什么是特性:就博主的理解,特性就是在类的类名称.属性.方法等上面加一个标记,使这些类.属 ...

  4. C#基础系列——小话泛型

    前言:前面两章介绍了C#的两个常用技术:C#基础系列——反射笔记 和 C#基础系列——Attribute特性使用 .这一章来总结下C#泛型技术的使用.据博主的使用经历,觉得泛型也是为了重用而生的,并且 ...

  5. C#基础系列——委托和设计模式(二)

    前言:前篇 C#基础系列——委托实现简单设计模式 简单介绍了下委托的定义及简单用法.这篇打算从设计模式的角度去解析下委托的使用.我们知道使用委托可以实现对象行为(方法)的动态绑定,从而提高设计的灵活性 ...

  6. C#基础系列——再也不用担心面试官问我“事件”了

    前言:作为.Net攻城狮,你面试过程中是否遇到过这样的问题呢:什么是事件?事件和委托的区别?既然事件作为一种特殊的委托,那么它的优势如何体现?诸如此类...你是否也曾经被问到过?你又是否都答出来了呢? ...

  7. C#基础系列——异步编程初探:async和await

    前言:前面有篇从应用层面上面介绍了下多线程的几种用法,有博友就说到了async, await等新语法.确实,没有异步的多线程是单调的.乏味的,async和await是出现在C#5.0之后,它的出现给了 ...

  8. C#基础系列——一场风花雪月的邂逅:接口和抽象类

    前言:最近一个认识的朋友准备转行做编程,看他自己边看视频边学习,挺有干劲的.那天他问我接口和抽象类这两个东西,他说,既然它们如此相像, 我用抽象类就能解决的问题,又整个接口出来干嘛,这不是误导初学者吗 ...

  9. c#基础系列(转)

    转:http://www.cnblogs.com/landeanfen/p/4953025.html C#基础系列——一场风花雪月的邂逅:接口和抽象类 前言:最近一个认识的朋友准备转行做编程,看他自己 ...

随机推荐

  1. Cuba获取属性文件中的配置

    最直接的办法是,使用AppContext.getProperty("cuba.trustedClientPassword"); 可以获取到系统中的web模块下的wep-app.pr ...

  2. CDQ分治学习笔记

    数据结构中的一块内容:$CDQ$分治算法. $CDQ$显然是一个人的名字,陈丹琪(NOI2008金牌女选手) 这种离线分治算法被算法界称为"cdq分治" 我们知道,一个动态的问题一 ...

  3. emwin之在WM_INIT_DIALOG分支下使用带触发功能的函数的程序框架

    @2018-08-29 [小记] 为避免在窗口创建时由于使用了带触发功能的函数导致执行一些在初始化过程中不允许的操作,特整理一个流程架构 --① 定义一个初始化完成的标志 unsigned ; --② ...

  4. Android设置RadioButton在文字的右边

    效果图如下: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:andro ...

  5. A1073. Scientific Notation

    Scientific notation is the way that scientists easily handle very large numbers or very small number ...

  6. 【LOJ#6280】数列分块4

    题目大意:维护一个长度为 N 的序列,支持两种操作:区间修改.区间求和.N <= 50000 题解:在维护分块的同时,维护每个区间的和,保证在 \(O(1)\) 的时间查询答案. 代码如下 #i ...

  7. 使用sass切图

    明日复明日,明日何其多!我生待明日,万事成蹉跎!ruby挺可爱的.没有进入工程项目中使用sass. 尝试是一件快乐的事情.在页面中直接引进编译出来的css即可.

  8. FTP文件乱码和传输模式解释

    转: FTP文件乱码和传输模式解释 2017年02月18日 10:50:03 -Hermes- 阅读数:12112更多 所属专栏: 异常解决方案急诊室   版权声明:大侠,在转载时请注明出处,小弟不胜 ...

  9. vue资源

    Vue中文官网:https://cn.vuejs.org/ Vue源码:https://github.com/vuejs/vue Vue官方工具:https://github.com/vuejs vu ...

  10. 面向对象【day07】:类的特性介绍(四)

    本节内容 1.概述 2.访问属性 3.私有属性 4.总结 一.概述 在上篇博客中我们已经讲了一些关于类的知识,我们来回顾以下: 定义类(class dog(object))-> 实例化(d = ...