一、定义
反射(Reflection)指程序可以访问、检测和修改它本身状态或者行为的一种能力。
程序包含模块、类型、成员,反射提供的是封装程序集、模块、类型、成员的对象。
二、作用
可以动态创建类型的实例,将类型绑定到现有对象;或者从现有对象中获取类型。然后,可以调用类型的方法或者访问其字段和属性。
三、使用
1.用途
- 它允许在运行时查看特性(attribute)信息
- 它允许审查集合中的各种类型,以及实例化这些类型
- 它允许延迟绑定的方法和属性(property)
- 它允许在运行时创建新类型,然后使用这些类型执行一些任务
2.命名空间
using System.Reflection;
using System.Type;
using System.Reflection.Assembly;
常用的为Type和Assembly两个类。
3.属性与方法
Type类
属性 | 说明 |
---|---|
Name | 数据类型名 |
FullName | 获取该类型的完全限定名称,包括其命名空间,但不包括程序集。 |
Namespace | 定义数据类型的命名空间名 |
Attribute | 获取与 Type 关联的属性 |
CustomAttributes | 获取包含此成员自定义属性的集合。(继承自 MemberInfo) |
方法 | 说明 |
---|---|
GetConstructors() | 返回ConstructorInfo类型数组,获取当前Type定义的所有公共构造函数 |
GetEvent(),GetEvents() | 返回EventInfo类型,获取该Type的事件信息 |
GetField(), GetFields() | 返回FieldInfo类型,获取该Type的字段(成员变量)的信息 |
GetInterface(), GetInterfaces() | 返回InterfaceInfo类型,获取该Type实现的接口的信息 |
GetMember(), GetMembers() | 返回MemberInfo类型,获取该Type的所有成员的信息 |
GetMethod(),GetMethods() | 返回MethodInfo类型,获取该Type的方法的信息 |
GetProperty(), GetProperties() | 返回PropertyInfo类型,获取该Type的属性的信息 |
GetEnumNames() | 返回当前枚举类型中各个成员的名称 |
更多请到Type官方文档…
Assembly类
Assembly类可以获取正在执行的程序集;可以动态加载程序集;以及在程序集中查找并创建类型实例。
使用:
//通过程序集名称返回Assembly对象
Assembly assembly = Assembly.Load("AssemblyName");
//通过dll文件名称返回Assembly对象
Assembly assembly = Assembly.LoadFrom("*.dll");
//通过Assembly对象获取具体类
Type t = assembly.GetType("ClassName"); //参数必须是全名
更多到 Assembly官方文档…
四、实例
using System;
using System.Reflection;
namespace ReflectionTest
{
class Student
{
public Student() { }
public Student(string name)
{
this.name = name;
}
public Student(string name, int age)
{
this.age = age;
this.name = name;
}
private string name;
private int age;
public string Name { get { return name; } set { name = value; } }
public int Age { get { return age; } set { age = value; } }
public void Show()
{
Console.WriteLine("name:{0},age{1}", this.name, this.age);
}
}
class Program
{
static void Main(string[] args)
{
Assembly assembly = Assembly.Load("ReflectionTest"); // 加载程序集
Type t = assembly.GetType("ReflectionTest.Student"); // 加载类
// 1. 查看类中的构造方法与参数
ConstructorInfo[] ciArr = t.GetConstructors(); // 获取类的所有构造函数
foreach (var ci in ciArr)
{
ParameterInfo[] paras = ci.GetParameters(); // 循环每个构造函数获取并打印参数
foreach (var para in paras)
{
Console.WriteLine(para.ParameterType.ToString() + "\n" + para.Name);
}
}
// 2. 查看类中的属性
PropertyInfo[] pis = t.GetProperties();
foreach (var pi in pis)
{
Console.WriteLine(pi.Name + "\n");
}
// 3. 用构造函数生成对象
Type[] types = new Type[pis.Length];
for (int i = 0; i < pis.Length; i++)
{
types[i] = pis[i].PropertyType;
}
ConstructorInfo ci0 = t.GetConstructor(types);
object[] o = new object[] { "盖伦", 22 };
object obj = ci0.Invoke(o);
((Student)obj).Show();
// 4. 用Activator生成对象
object[] o2 = new object[] { "亚索", 23 };
object obj2 = Activator.CreateInstance(t, o2);
((Student)obj2).Show();
// 5. 查看类中的方法(所有公共方法)
foreach (var mi in t.GetMethods())
{
Console.WriteLine(mi.Name + "");
}
// 6. 取得类中的方法并执行
object o3 = Activator.CreateInstance(t); // 创建一个类的实例
PropertyInfo nameF = t.GetProperty("Name"); // 获取Name属性
nameF.SetValue(o3, "瑞文");
PropertyInfo ageF = t.GetProperty("Age"); // 获取Age属性
ageF.SetValue(o3, 18);
MethodInfo methodInfo = t.GetMethod("Show");
methodInfo.Invoke(o3, null);
Console.ReadKey();
}
}
}
执行结果:
五、总结
- 优点
反射提高了程序的灵活性和拓展性;降低耦合性,提高自适应能力。它允许程序创建和控制任何类的对象,无需提供硬编码目标类。
- 缺点
1、性能问题:使用反射基本上是一种解释操作,包括了一些动态的类型,用于字段和方法接入时要远慢于直接代码,因此反射操作的效率要比非反射操作慢很多。
2、使用反射会模糊程序内内部逻辑:程序员希望在源代码中看到程序的逻辑,反射等绕过了源代码的技术,因而会带来维护问题。反射代码比相应的直接代码更复杂