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. 【转载】LCT

    原标题:LCT(Link-Cut Tree)详解(蒟蒻自留地) 出处:https://blog.csdn.net/saramanda/article/details/55253627 如果你还没有接触 ...

  2. CQOI2009叶子的染色

    叶子的染色 题目描述 给一棵m个结点的无根树,你可以选择一个度数大于1的结点作为根,然后给一些结点(根.内部结点和叶子均可)着以黑色或白色.你的着色方案应该保证根结点到每个叶子的简单路径上都至少包含一 ...

  3. [luogu1829][bzoj2154][国家集训队]Crash的数字表格 / JZPTAB【莫比乌斯反演】

    传送门:洛谷,bzoj 题目描述 今天的数学课上,Crash小朋友学习了最小公倍数(Least Common Multiple).对于两个正整数a和b,LCM(a, b)表示能同时整除a和b的最小正整 ...

  4. 洛谷 P1490 买蛋糕 解题报告

    P1490 买蛋糕 题目描述 野猫过生日,大家当然会送礼物了(咳咳,没送礼物的同志注意了哈!!),由于不知道送什么好,又考虑到实用性等其他问题,大家决定合伙给野猫买一个生日蛋糕.大家不知道最后要买的蛋 ...

  5. Java内存模型基础

    Java内存模型的基础 并发编程模型的两个关键问题 在并发编程种,需要处理两个关键问题:线程之间如何通信及线程之间如何同步(这里的线程是指并发执行的活动实体).通信是指线程之间以何种机制来交换信息.在 ...

  6. Fence Repair(poj3253)

    题目链接:http://poj.org/problem?id=3253 Description Farmer John wants to repair a small length of the fe ...

  7. outline与border的区别

    在浏览器里,当鼠标点击或使用Tab键让一个链接或者一个radio获得焦点的时候,该元素将会被一个轮廓虚线框围绕.这个轮廓虚线框就是 outline . outline 能告诉用户那一个可以激发事件的h ...

  8. libcurl使用easy模式阻塞卡死等问题的完美解决

    引言: 由于要在android手机测进行DM开发, 其中最重要的就是FUMO和SCOMO下载, 下载使用的是linux开源库libcurl. 于是就把libcurl的使用研究了一遍, 有些心得, 并解 ...

  9. vs widows服务的调试

    1.使用.net 工具安装你开发好的服务 2.服务运行后在Vs中选择调试>附加到进程 4.选择安装好运行的服务,选择附加

  10. css 实现背景图片不跟着滚动条滚动而滚动

    效果: 只需要在需要背景不跟着动的div里: div{ background:url(); background-attachment:fixed; } 加上background-attachment ...