一、反射(Reflection)
当我们只知道对象外部而不知道内部结构的情况下,通过C#的反射(Reflection)了解对象内部结构。C#中提供的反射(Reflection)机制可动态分析程序集、模块与类型,它不需要New一个对象,便可动态创建使用对象,从而降低代码耦合性。
反射(Reflection)内部实现依赖于元数据。元数据,简单来说,在公共语言运行时CLR中,是一种二进制信息,用来描述数据,数据的属性环境等等的一项数据,那么反射解析数据的内部实现通过元数据实现再合适不过了。
二、举例说明
我先随便写个需要反射的程序集:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StudentClass
{
public class Student
{
public Student()
{
}
public string Name { get; set; }
public int Age { get; set; }
public char Gender { get; set; }
public string IdCard { get; set; }
public string Address { get; set; }
private string Mobile { get; set; }
public void Eat()
{
Console.WriteLine("我喜欢吃西瓜!");
}
public void Sing()
{
Console.WriteLine("我喜欢唱歌!");
}
public int Calculate(int a, int b)
{
return a + b;
}
private string PrivateMethod()
{
return "我是一个私有方法";
}
}
}
我们来看一下看一下程序集、模块、类等信息:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace ReflectionInvoke
{
class Program
{
static void Main(string[] args)
{
//获取程序集信息
Assembly assembly = Assembly.LoadFile(@"E:\StudentClass\bin\Debug\StudentClass.dll");
Console.WriteLine("程序集名字:"+assembly.FullName);
Console.WriteLine("程序集位置:"+assembly.Location);
Console.WriteLine("运行程序集需要的额CLR版本:"+assembly.ImageRuntimeVersion);
Console.WriteLine("====================================================");
//获取模块信息
Module[] modules = assembly.GetModules();
foreach (Module item in modules)
{
Console.WriteLine("模块名称:"+item.Name);
Console.WriteLine("模块版本ID"+item.ModuleVersionId);
}
Console.WriteLine("======================================================");
//获取类,通过模块和程序集都可以
Type[] types = assembly.GetTypes();
foreach (Type item in types)
{
Console.WriteLine("类型的名称:"+item.Name);
Console.WriteLine("类型的完全命名:"+item.FullName);
Console.WriteLine("类型的类别:"+item.Attributes);
Console.WriteLine("类型的GUID:"+item.GUID);
Console.WriteLine("=====================================================");
}
//获取主要类Student的成员信息等
Type studentType = assembly.GetType("StudentClass.Student");//完全命名
MemberInfo[] mi = studentType.GetMembers();
foreach (MemberInfo item in mi)
{
Console.WriteLine("成员的名称:"+item.Name);
Console.WriteLine("成员类别:"+item.MemberType);
}
Console.WriteLine("=====================================");
//获取方法
BindingFlags flags = BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance;
MethodInfo[] methodInfo = studentType.GetMethods(flags);
foreach (MethodInfo item in methodInfo)
{
Console.WriteLine("public类型的,不包括基类继承的实例方法:"+item.Name);
}
Console.WriteLine("========================================");
BindingFlags flag = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic;
MethodInfo[] methods = studentType.GetMethods(flag);
foreach (MethodInfo item in methods)
{
Console.WriteLine("非public类型的,不包括基类继承的实例方法:"+item.Name);
}
Console.WriteLine("========================================");
//获取属性
BindingFlags flags2 = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly | BindingFlags.Instance;
PropertyInfo[] pi = studentType.GetProperties(flags2);
foreach (PropertyInfo item in pi)
{
Console.WriteLine("属性名称:"+item.Name);
}
}
}
}
结果:
1、Assembly.Load()以及Assembly.LoadFile():
LoadFile这个方法的参数是程序集的绝对路径,load方法有多个重载,还可以通过流的方式获取程序集,在项目中,主要用来取相对路径,因为很多项目的程序集会被生成在一个文件夹里,此时取相对路径不容易出错。
2、GetTypes和GetType():
很明显第一个获取程序集下所有的类,返回一个数组,第二个要有参数,类名为完全类名:命名空间+类名,用于获取指定的类。
3、Type类下可以获取这个类的所有成员,也可以获取字段属性方法等,有:
ConstructorInfo获取构造函数, FieldInfo获取字段, MethodInfo获取方法,PropertyInfo获取属性,EventInfo获取事件,ParameterInfo获取参数,通过他们的Get方法获取,加s获取所有返回数组,不加s获取具体的。
4、BindFlags:用于对获取的成员的类型加以控制:
BindingFlags.Public公共成员,NonPublic,非公有成员,DeclaredOnly仅仅反射类上声明的成员不包括简单继承的成员。CreateInstance调用构造函数,GetField获取字段值对setField无效。还有很多读者可以F12打开看一下用法以及注释。注意必须指定:BindingFlags.Instance或BindingFlags.Static,主要为了获取返回值,是静态的还是实例的。