目录
一、概述
我们之所以能看见这个世界万事万物,不是万物能发光,而是因为它们反射了太阳的光芒。在C#代码世界里,我们的代码被编译为一个个DLL和EXE ,同样,也有这样一束光能让我们看清DLL和EXE中我们定义的各个类/属性等等一切。这束光就是 using System.Reflection; 看清并使用里面的内容的过程就是反射。
二、基本使用
2.1 获取程序类容基本步骤
a. 加载程序集
b.获得程序集中类型
c.获得其中属性/方法/字段
基本操作代码如下
{
//第一步,根据{程序集名称}加载程序集,可以指定完整路径,否则从当前运行目录寻找
Assembly assembly = Assembly.Load("XiaoMiTV");
//第二步,获取程序中所有的类型TYPE
Type[] types = assembly.GetTypes(); //获取程序集中所有的Type,也就是程序集中包含的所有C#类型(类,接口,委托,结构...)
Type type = assembly.GetType("XiaoMiTV.XiaoMi"); //获取指定Type名称(包含命名空间)的Type
//第三步,获取Type中的类容
PropertyInfo[] props = type.GetProperties(); //获得type中的所有属性
var prop = type.GetProperty("propName"); //根据指定名称获取属性
FieldInfo[] fields = type.GetFields(); //获取type中的所有字段
FieldInfo field = type.GetField("FildName"); //根据指定名称获取字段
MethodInfo[] methods = type.GetMethods(); //获取type中所有方法
MethodInfo method = type.GetMethod("MethodName"); //根据指定名称获取方法
}
2.2 使用举例
2.2.1 创建对象&访问设置属性值&使用其中无参方法
var assembly = Assembly.Load("XiaoMiTV");
var type = assembly.GetType("XiaoMiTV.XiaoMi");
/*
* 创建对象实例
* CreateInstance(Type type,params object[] args)
* 参数:Type => 要创建实例的类型
* params object[] => 构造函数参数组,无参构造传入null
*/
object objTv = Activator.CreateInstance(type, null);
// objTv.Id = 5; // 编译报错,object 并没有Id属性
var prop = type.GetProperty("Id"); // 根据Id获取 Id属性
prop.SetValue(objTv, 5); // 设置objTv对象中Id属性值
var propValue = prop.GetValue(objTv); // 获取objTv对象中Id属性值
var Show = type.GetMethod("Show"); //获取type中的Show方法
/*
* 执行Show方法
* XXMethod.Invoke(object obj,object[] args)
* 参数:
* object obj =>该方法执行载体,在哪个具体实例中执行
* object[] args =>执行该方法的参数组,无参数则为null
*/
Show.Invoke(objTv, null);
注意事项:
1. Activator.CreateInstance(type,null);返回的是object对象,无法直接访问Id属性,可以转换如下,但是不推荐。后续文章会有专门的解决方案
//用dynamic 替换 object ,躲过编译器检查
//但是会出现安全问题,如果选择了一个不存在的属性,运行的时候就报错
dynamic dynamicTv = Activator.CreateInstance(type, null);
dynamicTv.Id = 3;
2. XXMethod.Invoke(object obj,object[] args); obj 参数,执行载体,其实就是为这个方法提供执行上下文。执行上下文不一样,结果可能会不一样(如果它用到载体中的一些元素)
3. prop.SetValue() 和 prop.GetValue() 一定要指定到具体对象实例
2.2.2 创建带参构造函数并访问其中带参方法
{
var assembly = Assembly.Load("XiaoMiTV");
var type = assembly.GetType("XiaoMiTV.XiaoMi");
var obj = Activator.CreateInstance(type, new object[] { "Nemo" }); //带一个string类型的构造参数
var ShutDown = type.GetMethod("ShutDown");
var re = ShutDown.Invoke(obj, new object[] { 5 }); // 带一个int类型的方法
}
注意事项:
1. 我只举例单个参数,多个参数的话,直接在 new object[]{}中依次添加。
2. 若方法有返回值,直接也可以获取,不过是 object类型,使用的是后进行强转。
2.2.3 反射重载过的方法
就是在寻找方法的时候加上重载类型如下:
/* 反射如下Reload的方法
* public void Reload(string name,int sec);
*/
var Reload = type.GetMethod("Reload", new Type[] { typeof(string), typeof(int) });
/* 反射如下Reload的方法
* public void Reload(int sec);
*/
var Reload1 = type.GetMethod("Reload", new Type[] { typeof(int) });
2.2.4 反射泛型类和泛型方法
var assembly = Assembly.Load("XiaoMiTV");
var GenericType = assembly.GetType("XiaoMiTV.GenericT`1") //`1 表示该类有一个占位(泛型参数)
.MakeGenericType(new Type[] { typeof(int) }); //表示要创建对象实例时传入的指定类型为int
var GenericMethod = GenericType.GetMethod("MethTest")
.MakeGenericMethod(new Type[] { typeof(int) }); //指定调用该方法传入的泛型类型为string
GenericMethod.Invoke(GenericType, new object[] { 2 }); //调用泛型方法
注意事项:
1. 占位符用 `2 表示有两个泛型类型。“`” 是键盘第二排第一个英文状态下的符号。
2.在指定泛型具体类型时候要注意和原对象泛型顺序一致。
2.2.5 反射类型中的私有类容(属性&方法&...)
var methodShow = typeSingleton.GetMethod("Show",BindingFlags.NonPublic | BindingFlags.Instance);
就是在 GetXXX 方法中添加 BindingFlags 选项。还有很多其他选项,需要的时候可以看看
2.2.6 反射类型中的Attribute
var assembly = Assembly.Load("XiaoMiTV");
var type = assembly.GetType("XiaoMiTV.XiaoMi");
IEnumerable<Attribute> typeAttrs = type.GetCustomAttributes(); //获取对象上的所有Attribute
var prop = type.GetProperty("Id");
IEnumerable<Attribute> propAttrs = prop.GetCustomAttributes(); //获取属性上的所有Attribute
var Method = type.GetMethod("Show");
IEnumerable<Attribute> methodAttrs = Method.GetCustomAttributes(); //获取方法上的所欲Attribute
注意事项:
XX.GetCustomAttibutes中获取一个枚举类型的结果,然后可以进一步处理(下面章节Attribut会提到)
三 、反射特点
1.动态加载dll,根据字符串就可以创建对象实例并使用,减少了对象间依赖
2.突破私有权限,可以访问私有内容(方法$属性...)
3.性能较差