目录
1.背景
2.定义
3.利弊
4.Type
5.反射程序集
6.反射基本类型
7.反射特性
8.动态创建对象
9.动态调用方法
背景
反射不常用,但是在开发一些基础框架或公共类库,使用反射会使系统更加灵活,有时甚至是唯一的选择。反射是个庞大的话题,牵扯到的知识点也很多,包括程序集,自定义特性,泛型等等。
定义
1.提供了在运行时对类型和类型成员的元数据(metadata)的访问能力。
2.一般会通过System.Reflaction名称空间并配合Type类来实现。
3.Type类封装了类型的元数据,是进行反射的入口
功用
1.查看和遍历类型和成员的元数据
2.动态创建类型实例,动态调用所创建实例的方法、字段、属性
3.延迟绑定方法和属性
利弊
1.不足
1.1 开销很大,性能不佳
2.优点
2.1 灵活
3.总结
牺牲性能换灵活,这是一种折中的选择
Type类
//获取Type实例三种方法:
1.1 Type t = Type.GetType("System.IO.Stream");1.2 Type t = typeof(System.IO.Stream);1.3 Type t = "Hello".GetType();
//Type类型的基本信息
Type t = "Hello".GetType();t.Name; //获取类型名称t.FullName;//获取类型全名t.Namespace;//获取名称空间t.BaseType;//获取对基类的Type类型的引用t.UnderlyingSystemType;t.Attributes;//获取TypeAttributes位标记t.IsValueType;t.IsByRef;t.IsEnum;t.IsClass;t.IsInterface;t.IsSealed;t.IsPrimitive;//是否基类型t.IsAbstract;t.IsPublic;t.IsVisible;……
//Type类型的成员信息
//包含哪些字段,字段名称,类型,可访问性--FieldInfo
//包含哪些属性,属性名称,类型,可访问性--PropertyInfo
//包含哪些方法,方法名称,返回值,参数个数,参数类型,参数名称--MethodInfo
//事件 EventInfo 参数ParameterInfo
t.GetFields();t.GetProperties();t.GetMethods();t.GetProperties();t.GetConstructor();t.GetMembers(BindingFlags.ExactBinding | BindingFlags.DeclaredOnly);……
//位标记
//.NET中的枚举我们一般有两种用法:
//一是表示唯一的元素序列,例如一周里的各天;
//还有就是用来表示多种复合的状态,这个时候一般需要为枚举加上[Flags]特性标记为位域
//例如:
[Flags]public enum BindingFlags{Default = 0,IgnoreCase = 1,DeclaredOnly = 2,Instance = 4,Static = 8,Public = 16,NonPublic = 32,FlattenHierarchy = 64,InvokeMethod = 256,CreateInstance = 512,GetField = 1024,SetField = 2048}
反射程序集
1.获取Assembly的几种方法
1.1 Assembly am = Assembly.LoadFrom("Demo.dll");1.2 Assembly am = Assembly.Load("Demo");1.3 Assembly am = typeof(int).Assembly;1.4 获取当前程序集Assembly am = Assembly.GetExecutingAssembly();am.FullName;//程序集名称am.Location; //路径am.GetTypes();//全部类型am.GetType();//某个类型am.GetModules();am.GetModule();am.GetCustomAttributes();Module[] modules = am.GetModules();foreach (Module module in modules){Console.WriteLine("模块:" + module);//Demo.dllforeach (Type t in module.GetTypes())Console.WriteLine("类型:" + module);//Demo.BaseClass Demo.Delegate Demo.Struct Demo.Interface Demo.DemoClass}
反射基本类型
因为程序集包含很多类型,一个类型又包含很多成员,一个成员又包含很多其他信息,层层嵌套,为了阅读方便,去掉最外套的循环。
1.获取基本类型
2.获取成员信息和MemberInfo类型
3.字段信息和FieldInfo
4.属性信息和PropertyInfo
5.方法信息和MethodInfo
6.……
//反射枚举
public static class EnumManager {private static DataTable GetDataTable(){Type t = typeof (TEnum);FieldInfo[] fieldInfos = t.GetFields();DataTable table=new DataTable();table.Columns.Add("Name", Type.GetType("System.String"));table.Columns.Add("Value", Type.GetType("System.Int32"));foreach (FieldInfo field in fieldInfos){if (field.IsSpecialName){DataRow row = table.NewRow();row[0] = field.Name;row[1] = Convert.ToInt32(field.GetRawConstantValue());table.Rows.Add(row);}}return table;}}
反射特性
特性:特性是一种特殊的类型,可以加载到程序集的各种类型上,包括模块,类,接口,结构,构造函数,方法,方法参数等。特性是为程序集添加元数据的一种机制,通过他可以为编译器提供指示或对数据的说明。
1.自定义特性:
//AllowMultiple=true表示可以重复添加到一个类型上
[AttributeUsage(AttributeTargets.Class,AllowMultiple=true,Inherited = false)]public class RecordAttribute:Attribute{private string recordType;private string author;private DateTime date;private string memo;public RecordAttribute(string recordType, string author, string date){this.recordType = recordType;this.author = author;this.date = Convert.ToDateTime(date);}public string RecordType { get { return recordType; } }public string Author { get { return author; } }public DateTime Date { get { return date; } }public string Memo { get { return memo; }set { memo = value; }}}
2.使用特性
//特性使用:仅写成一行,不管是构造函数参数还是属性,全部写到构造函数的圆括号中
//对应属性,采用“属性=值”的格式
[Record("更新","jackyfei","2016-08-24")][Record("创建","张飞洪","2016-08-14",Memo = "这个类是演示用的")]public class DemoClass{}
3.反射特性
//使用特性对类型进行标记,目的是为了在程序中的某处使用它。
Type t = typeof(DemoClass);object[] records = t.GetCustomAttributes(typeof (RecordAttribute), false);foreach (RecordAttribute record in records){Console.WriteLine("{0}", record);Console.WriteLine(" 类型:{0}", record.RecordType);Console.WriteLine(" 作者:{0}", record.Author);Console.WriteLine(" 日期:{0}", record.Date.ToShortDateString());Console.WriteLine(" 备注:{0}", record.Memo);}
//获取到特性对象后,就可以任意处理了,比如说导出程序注释
动态创建对象
前面的知识内容,主要是利用反射查看类型,查看特性,程序集,都是属于查看元数据的信息的内容。接下来将学习如何用反射动态创建一个对象。之所以叫动态,是因为可以字符串可以由各种途径获得
[Record("更新", "jackyfei", "2016-08-24")][Record("创建", "张飞洪", "2016-08-14", Memo = "这个类是演示用的")]public class Calculator{private int x;private int y;public Calculator(){x = 0;y = 0;Console.WriteLine("x:{0},y:{1}", x, y);}public Calculator(int x,int y){this.x = x;this.y = y;Console.WriteLine("x:{0},y:{1}",x,y);}public string GetStr(){return "hello,jackyfei";}public int Add(){int total = 0;total = x + y;return total;}public static int Add(int x,int y){int total = x + y;return total;}
1. 使用无参构造函数创造对象
//创建对象一Assembly am = Assembly.GetExecutingAssembly();DemoClass demoClass= am.CreateInstance("NameSpace.DemoClass", true) as DemoClass;Console.WriteLine(demoClass.GetStr());//创建对象二//第一个参数是程序集的名称,null表示当前程序集;ObjectHandle handler = Activator.CreateInstance(null, "NameSpace.DemoClass");handler.Unwrap();
2. 使用有参构造函数创造对象
Assembly am2 = Assembly.GetExecutingAssembly();Object[] parameters=new object[2];parameters[0] = 3;parameters[1] = 5;//BindingFlags用于限定类型成员的搜索,Default意思是不使用这个策略am2.CreateInstance("NameSpace.DemoClass", true, BindingFlags.Default,null, parameters, null, null);
动态调用方法
这里的调用方法不是将上面的动态创建的对象强制类型转换后再调用,而且利用发射,基于字符串来调用。
1.1 InvokeMember()调用方式
Type t = typeof (Calculator);Calculator c=new Calculator(3,5);int rst =(int)t.InvokeMember("Add", BindingFlags.InvokeMethod, null, c, null);Console.WriteLine("x+y={0}", rst);object[] p = {6, 7};int rst2 = (int)t.InvokeMember("Add", BindingFlags.InvokeMethod, null, t, p);Console.WriteLine("x+y={0}", rst2);
//调用静态方法,他不是基于某个具体的类型实例,而是基于类型本身,
//所以传递的参数是typeof(Calculator)
1.2 GetMethod()调用方式
Type t = typeof (Calculator);Calculator c=new Calculator(3,5);//BindingFlags.Instance 调用实例方法MethodInfo mi = t.GetMethod("Add", BindingFlags.Instance | BindingFlags.Public);int rst = (int)mi.Invoke(c, null);Console.WriteLine("x+y={0}", rst);//BindingFlags.Static 调用静态方法object[] p = { 6, 9 };MethodInfo mi2 = t.GetMethod("Add", BindingFlags.Static | BindingFlags.Public);int rst2 = (int)mi2.Invoke(null, p);Console.WriteLine("x+y={0}", rst2);