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. xslt格式化日期的方法

    数据:<PK_SEND_DATE>2007-9-28 0:00:00</PK_SEND_DATE> 通过截取:<xsl:value-of select="sub ...

  2. servlet表单的get和post方法的实现

    几经周折,这个简单的小程序终于实现了,全新的编译环境和领域,适应起来有点慢,学习能力还是有待提高 使用IDEA2017.3.3创建简单的servlet程序: 1.创建一个项目 file - new p ...

  3. INSTALL_FAILED_INSUFFICIENT_STORAGE 的解决方法

    今天用真机测试,出现了下面的情况 , 网上找了好多情况 , 都感觉比较费事,没什么效果,报错依旧, [2013-08-06 16:31:04 - Flashlight] Installation er ...

  4. BZOJ 3164: [Heoi2013]Eden的博弈问题

    3164: [Heoi2013]Eden的博弈问题 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 134  Solved: 98[Submit][St ...

  5. 使用FreeRTOS在SD卡驱动使用非系统延时导致上电重启不工作的情况

    一.问题描述在一个使用FreeRTOS的工程中,只做了SD卡的驱动,由于RTOS使用了Systick,故非系统延时函数使用的是 DWT中的时钟周期(CYCCNT)计数功能,但是在SD卡驱动中使用了这个 ...

  6. React Native——组件的生命周期

    组件生命周期 上流程图描述了组件从创建.运行到销毁的整个过程,可以看到如果一个组件在被创建,从开始一直到运行会依次调用getDefaultProps到render这五个函数:在运行过程中,如果有属性和 ...

  7. CISCN2018-WP

    MISC: 验证码: 用token登录 输入好验证码就可以得到flag Picture: 图片隐写,一下就想到binwalk或者winhex打开试试 binwalk打开无果 将这段数据ctrl+shi ...

  8. hdu 2844 coins(多重背包 二进制拆分法)

    Problem Description Whuacmers use coins.They have coins of value A1,A2,A3...An Silverland dollar. On ...

  9. 【LOJ#10002】喷水装置

    题目大意:给定一段区间 [l,r] ,N 条线段,求至少用多少条线段能够覆盖整个区间,不能覆盖输出-1. 题解:每次在起点小于当前位置的线段集合中选择有端点最大的位置作为下一个位置,并更新答案,如果当 ...

  10. eclipse如何以指定JDK启动

    eclipse如何以指定JDK启动 2018年01月11日 14:30:23 buerc 阅读数:11179   版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csd ...