第六节:反射(几种写法、好处和弊端、利用反射实现IOC)
2017-05-22 20:42
267 查看
一. 加载dll,读取相关信息
1. 加载程序集的三种方式
调用Assembly类下的三个方法:Load、LoadFile、LoadFrom。
2. 获取程序集中所有的类
通过方法GetTypes来实现。
通过方法GetType("类名全写")来实现获取单个类(DBHelper)。
3. 获取类中的所有构造函数
通过方法GetConstructors来实现。
4. 获取类中的所有属性
通过方法GetProperties来实现。
5. 获取类中的所有方法
通过方法GetMethods来实现。
6. 获取类中的所有接口
通过方法GetInterfaces来实现。
[b]二. 反射创建对象[/b]
1. 反射创建对象
通过Activator.CreateInstance()方法来创建对象
1.1 ReflectionTest类的代码
1.2 反射创建对象的代码
2. 反射破坏单例,调用私有构造函数
单例代码
破坏单例,调用私有构造函数代码
3. 反射创建泛型(扩展)
[b]三. IOC(反射+简单工厂+配置文件)[/b]
背景:有三套相同的数据库,分别是SQLServer、MySQL、Oracle数据库,要求可以分别连接这三个不同的数据库,并且发布后,可以在不重新发布的情况下,切换连接数据库。
对应上述背景,建立相应的解决方案,目录如下,并介绍介绍几种传统的解决方案
方案一:在Reflection中直接添加对DB.SQLServer的引用
方案二:在Reflection中直接添加对DB.SQLServer、DB.Interface的引用
点评:以上两种方案实质上都是通过对相应的实现类添加引用,new出来对象,然后调用方法来实现,没法发布后动态修改数据库。
方案三:通过反射来创建对象,只需要添加对DB.Interface的引用即可,但需要把DB.SQLServer、[b]DB.MySQL、DB.Oracle的生成dll的目录改成Reflection程序下[/b]
点评:该方案只需要对接口添加引用,符合了面向接口编程的思想,但是发布后在不修改代码的情况下,不能切换数据库。
方案四:IOC(反射+简单工厂+配置文件),[b]需要添加对DB.Interface的引用,并且把DB.SQLServer、[b]DB.MySQL、DB.Oracle的生成dll的目录改成Reflection程序下[/b][/b]
配置文件:
简单工厂:
调用:
[b]四. 反射调用实例方法、静态方法、重载方法[/b]
[b]五. 反射字段和属性,获取值和设置值[/b]
[b]六. 反射的好处和局限[/b]
运行结果:
[b] 七. 补充:获取类的属性、方法、特性、构造函数等,设置属性值,获取属性值[/b]
函数 说明
GetConstructor(s) 取得此类型的创建函数,其将回传一个ConstructorInfo对象或数组
GetField(s) 取得此类型中成员变量,其将回传一个FiledInfo对象或数组
GetMember(s) 取得此类中的成员,其类型可以是变量、事件、属性、方法及Nested Type,其将回传一个MemberInfo对象或数组
GetEvent(s) 取得此类型中的事件,其将回传一个EventInfo对象或数组
GetProperty/GetProperties 取得此类型中的属性,其将回传一个PropertyInfo对象或数组
GetNestedType(s) 取得声明于此类型内类型,其将回传一个Type对象或数组
GetCustomAttibutes 取得绑定于此类型的Attitudes
GetValue(t) 获取t对象的的属性值
SetValue(t,"XXX") 设置t对象的属性值为XXX
实体代码:
调用代码:
结果:
1. 加载程序集的三种方式
调用Assembly类下的三个方法:Load、LoadFile、LoadFrom。
//1.1 Load方法:动态默认加载当前路径下的(bin)下的dll文件,不需要后缀 Assembly assembly = Assembly.Load("DB.SQLServer"); //1.2 LoadFile方法:程序集的绝对路径 Assembly assembly2 = Assembly.LoadFile(@"D:\我的框架之路\DotNet体系\02-DotNet进阶\02-反射\01-code\Reflection\bin\Debug\DB.SQLServer.dll"); //1.3 LoadFrom方法:可以是当前路径(需要写上后缀.dll),也可以是绝对路径 Assembly assembly3 = Assembly.LoadFrom("DB.SQLServer.dll");
2. 获取程序集中所有的类
通过方法GetTypes来实现。
Assembly assembly = Assembly.Load("DB.SQLServer"); Type[] t1 = assembly.GetTypes();
通过方法GetType("类名全写")来实现获取单个类(DBHelper)。
Type tItem = assembly.GetType("DB.SQLServer.DBHelper");
3. 获取类中的所有构造函数
通过方法GetConstructors来实现。
//1.加载程序集 Assembly assembly = Assembly.Load("DB.SQLServer"); //2.获取程序集中的特定类 Type tItem = assembly.GetType("DB.SQLServer.DBHelper"); //3.获取特定类中的所有构造函数 ConstructorInfo[] cInfor = tItem.GetConstructors();
4. 获取类中的所有属性
通过方法GetProperties来实现。
//1.加载程序集 Assembly assembly = Assembly.Load("DB.SQLServer"); //2.获取程序集中的特定类 Type tItem = assembly.GetType("DB.SQLServer.DBHelper"); //3.获取特定类中的所有属性 PropertyInfo[] propertyInfo = tItem.GetProperties();
5. 获取类中的所有方法
通过方法GetMethods来实现。
//1.加载程序集 Assembly assembly = Assembly.Load("DB.SQLServer"); //2.获取程序集中的特定类 Type tItem = assembly.GetType("DB.SQLServer.DBHelper"); //3.获取特定类中的所有方法 MethodInfo[] methordInfo = tItem.GetMethods();
6. 获取类中的所有接口
通过方法GetInterfaces来实现。
//1.加载程序集 Assembly assembly = Assembly.Load("DB.SQLServer"); //2.获取程序集中的特定类 Type tItem = assembly.GetType("DB.SQLServer.DBHelper"); //3.获取特定类中的所有接口 Type[] type = tItem.GetInterfaces();
[b]二. 反射创建对象[/b]
1. 反射创建对象
通过Activator.CreateInstance()方法来创建对象
1.1 ReflectionTest类的代码
public class ReflectionTest { public int Id { get; set; } public string Name { get; set; } public string Field = null; public static string FieldStatic = null; #region 构造函数 public ReflectionTest() { Console.WriteLine("这里是{0}无参数构造函数", this.GetType()); } public ReflectionTest(string name) { Console.WriteLine("这里是{0} 有1个参数构造函数", this.GetType()); } public ReflectionTest(int id, string name) { Console.WriteLine("这里是{0} 有2个参数构造函数", this.GetType()); } #endregion public void Show1() { Console.WriteLine("这里是{0}的Show1", this.GetType()); } public void Show2(int id) { Console.WriteLine("这里是{0}的Show2", this.GetType()); } public static void ShowStatic(string name) { Console.WriteLine("这里是{0}的ShowStatic", typeof(ReflectionTest)); } public void Show3() { Console.WriteLine("这里是{0}的Show3_1", this.GetType()); } public void Show3(int id, string name) { Console.WriteLine("这里是{0}的Show3", this.GetType()); } public void Show3(string name, int id) { Console.WriteLine("这里是{0}的Show3_2", this.GetType()); } public void Show3(int id) { Console.WriteLine("这里是{0}的Show3_3", this.GetType()); } public void Show3(string name) { Console.WriteLine("这里是{0}的Show3_4", this.GetType()); } private void Show4(string name) { Console.WriteLine("这里是{0}的Show4", this.GetType()); } public void ShowGeneric<T>(T name) { Console.WriteLine("这里是{0}的ShowStatic T={1}", this.GetType(), typeof(T)); } }
1.2 反射创建对象的代码
//1.加载程序集 Assembly assembly = Assembly.Load("DB.SQLServer"); //2.获取程序集中的特定类 Type tItem = assembly.GetType("DB.SQLServer.ReflectionTest"); //3.1 无参构造函数 Activator.CreateInstance(tItem); //3.2 一个参数的构造函数 Activator.CreateInstance(tItem ,"1"); //3.3 两个参数的构造函数 Activator.CreateInstance(tItem , 1,"2");
2. 反射破坏单例,调用私有构造函数
单例代码
public sealed class Singleton { private Singleton() { Console.WriteLine("初始化一次"); } private static Singleton Instance = new Singleton(); public static Singleton CreateInstance() { return Instance; } }
破坏单例,调用私有构造函数代码
//1.加载程序集 Assembly assembly = Assembly.Load("DB.SQLServer"); //2. 获取单例类 Type tc3 = assembly .GetType("DB.SQLServer.Singleton"); //3. 创建对象 Activator.CreateInstance(tc3, true);
3. 反射创建泛型(扩展)
//1.加载程序集 Assembly assembly = Assembly.Load("DB.SQLServer"); //2. 获取单例类 Type tc4 = assembly4.GetType("DB.SQLServer.GenericClass`1"); tc4 = tc4.MakeGenericType(typeof(int)); Activator.CreateInstance(tc4);
[b]三. IOC(反射+简单工厂+配置文件)[/b]
背景:有三套相同的数据库,分别是SQLServer、MySQL、Oracle数据库,要求可以分别连接这三个不同的数据库,并且发布后,可以在不重新发布的情况下,切换连接数据库。
对应上述背景,建立相应的解决方案,目录如下,并介绍介绍几种传统的解决方案
方案一:在Reflection中直接添加对DB.SQLServer的引用
Console.WriteLine("------------------------------------1. 传统方式调用DBHelper中的Query方法--------------------------------------"); //1.传统的方式调用(需要对 DB.SQLServer添加引用) DBHelper db1 = new DBHelper("123"); db1.Query();
方案二:在Reflection中直接添加对DB.SQLServer、DB.Interface的引用
Console.WriteLine("------------------------------------2. 接口的方式调用--------------------------------------"); //2. 接口的方式调用(只需要引用接口的程序集即可) IDBHelper idb1 = new DBHelper("123"); idb1.Query();
点评:以上两种方案实质上都是通过对相应的实现类添加引用,new出来对象,然后调用方法来实现,没法发布后动态修改数据库。
方案三:通过反射来创建对象,只需要添加对DB.Interface的引用即可,但需要把DB.SQLServer、[b]DB.MySQL、DB.Oracle的生成dll的目录改成Reflection程序下[/b]
Console.WriteLine("------------------------------------3. 反射的方式创建对象--------------------------------------"); //3. 反射的方式创建对象(不需要直接添加对其引用,只需要把相应的程序生成路径改成Reflection中即可) Assembly assembly4 = Assembly.Load("DB.SQLServer"); Type tc = assembly4.GetType("DB.SQLServer.DBHelper"); //object myDbHelper = Activator.CreateInstance(tc, "123"); //调用带参数的构造函数 object myDbHelper = Activator.CreateInstance(tc); //默认调用无参构造函数 IDBHelper idb2 = (IDBHelper)myDbHelper; idb2.Query();
点评:该方案只需要对接口添加引用,符合了面向接口编程的思想,但是发布后在不修改代码的情况下,不能切换数据库。
方案四:IOC(反射+简单工厂+配置文件),[b]需要添加对DB.Interface的引用,并且把DB.SQLServer、[b]DB.MySQL、DB.Oracle的生成dll的目录改成Reflection程序下[/b][/b]
配置文件:
<appSettings> <!--直接修改配置文件,可以修改数据库连接牛逼,可以直接切换 oracle 、mysql数据库,发布后可以直接通过改配置文件,切换数据库,代码什么也不用改,体会:反射+面向接口编程--> <!--前提:相应的DBHelper类必须满足接口约束,需要把Oracle或MySql的dll文件拷贝到Reflection中的bin文件中 --> <!--SQLServer改为: --> <!--<add key="IDBHelper-dllName" value="DB.SQLServer"/> <add key="IDBHelper-className" value="DB.SQLServer.DBHelper"/>--> <!--Oracle改为: --> <!--<add key="IDBHelper-dllName" value="DB.Oracle"/> <add key="IDBHelper-className" value="DB.Oracle.DBHelper"/>--> <!--MySql改为: --> <add key="IDBHelper-dllName" value="DB.MySql"/> <add key="IDBHelper-className" value="DB.MySql.DBHelper"/> </appSettings>
简单工厂:
/// <summary> /// 简单工厂,创建对象 /// </summary> public class SimpleFactory { private static string IDBHelperdllName = ConfigurationManager.AppSettings["IDBHelper-dllName"]; private static string IDBHelperClassName = ConfigurationManager.AppSettings["IDBHelper-className"]; public static IDBHelper CreateDBHelper() { Assembly assembly = Assembly.Load(IDBHelperdllName); Type type = assembly.GetType(IDBHelperClassName); object obj = Activator.CreateInstance(type); return (IDBHelper)obj; } }
调用:
Console.WriteLine("------------------------------------4. IOC(反射+简单工厂+配置文件)--------------------------------------"); //4. IOC(反射+简单工厂+配置文件)(不需要直接添加对其引用,只需要把相应的程序生成路径改成Reflection中即可) IDBHelper idb3 = SimpleFactory.CreateDBHelper(); idb3.Query();
[b]四. 反射调用实例方法、静态方法、重载方法[/b]
//2. 实例方法、静态方法、重载方法的调用 { object obj = Activator.CreateInstance(tc5); Console.WriteLine("---------------------------------2.1 调用无参、有参的实例方法-------------------------------------"); //2.1 调用无参、有参的实例方法 { MethodInfo methord = tc5.GetMethod("Show1"); methord.Invoke(obj, null); } { MethodInfo methord = tc5.GetMethod("Show2"); methord.Invoke(obj, new object[]{11}); } Console.WriteLine("---------------------------------2.2 调用静态方法-------------------------------------"); //2.2 调用静态方法 { MethodInfo methord = tc5.GetMethod("ShowStatic"); methord.Invoke(obj, new object[] { "ShowStatic1234" }); } Console.WriteLine("---------------------------------2.3 调用重载方法(需要在创建方法的时候,把类型传进去)-------------------------------------"); //2.3 调用重载方法(需要在创建方法的时候,把类型传进去) { MethodInfo methord = tc5.GetMethod("Show3", new Type[] { }); methord.Invoke(obj, null); } { MethodInfo methord = tc5.GetMethod("Show3", new Type[] { typeof(int)}); methord.Invoke(obj, new object[] { 11 }); } { MethodInfo methord = tc5.GetMethod("Show3", new Type[] { typeof(string) }); methord.Invoke(obj, new object[] { "11" }); } { MethodInfo methord = tc5.GetMethod("Show3", new Type[] { typeof(int), typeof(string) }); methord.Invoke(obj, new object[] { 11 ,"11"}); } { MethodInfo methord = tc5.GetMethod("Show3", new Type[] { typeof(string), typeof(int) }); methord.Invoke(obj, new object[] { "11",11 }); } Console.WriteLine("---------------------------------2.4 调用私有方法-------------------------------------"); //2.4 调用私有方法 { MethodInfo methord = tc5.GetMethod("Show4", BindingFlags.Instance|BindingFlags.NonPublic); methord.Invoke(obj, new object[] { "11" }); } Console.WriteLine("---------------------------------2.5 调用泛型方法-------------------------------------"); //2.5 调用泛型方法 { MethodInfo methord = tc5.GetMethod("ShowGeneric"); methord = methord.MakeGenericMethod(typeof(string)); methord.Invoke(obj, new object[] { "123" }); } }
[b]五. 反射字段和属性,获取值和设置值[/b]
{ //实例化对象 ReflectionTest rTest = new ReflectionTest(); //反射 Assembly assembly5 = Assembly.Load("DB.SQLServer"); Type type5 = assembly5.GetType("DB.SQLServer.ReflectionTest"); object object5 = Activator.CreateInstance(type5); //1.获取类的所有属性 Console.WriteLine("------------------------------------1.获取类的属性--------------------------------------"); var pInfor= type5.GetProperties(); foreach (var item in pInfor) { Console.WriteLine(item.Name); if (item.Name.Equals("Id")) { item.SetValue(object5, 12332); } } Console.WriteLine("------------------------------------输出ID属性值--------------------------------------"); rTest = (ReflectionTest)object5; Console.WriteLine(rTest.Id); //2.获取类的字段 Console.WriteLine("------------------------------------2.获取类的字段--------------------------------------"); foreach (var item in type5.GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public)) { Console.WriteLine(item.Name); } }
[b]六. 反射的好处和局限[/b]
{ //反射的好处: //反射是.NET中的重要机制,通过反射,可以在运行时获得程序或程序集中每一个类型(包括类、结构、委托、接口和枚举等)的成员和成员的信息。 //有了反射,即可对每一个类型了如指掌。另外我还可以直接创建对象,即使这个对象的类型在编译时还不知道。 //性能局限 Console.WriteLine("------------------------------测试普通方法和反射的耗时情况--------------------------------------"); { //1.普通方法 Stopwatch watch = new Stopwatch(); watch.Start(); for (var i = 0; i < 1000000; i++) { DBHelper2 dh = new DBHelper2(); dh.Id = 1; dh.Name = "maru"; dh.Query(); } watch.Stop(); Console.WriteLine("普通方式花费{0}ms:",watch.ElapsedMilliseconds); } { //2. 反射的方法 Stopwatch watch = new Stopwatch(); watch.Start(); Assembly assembley6 = Assembly.Load("DB.SQLServer"); Type type6 = assembley6.GetType("DB.SQLServer.DBHelper2"); for (var i = 0; i < 1000000; i++) { object obj6 = Activator.CreateInstance(type6); foreach (var item in type6.GetProperties()) { if (item.Name.Equals("Id")) { item.SetValue(obj6, 1); } if (item.Name.Equals("Name")) { item.SetValue(obj6, "maru"); } } MethodInfo m6 = type6.GetMethod("Query"); m6.Invoke(obj6, null); } watch.Stop(); Console.WriteLine("反射方式花费{0}ms:", watch.ElapsedMilliseconds); } }
运行结果:
[b] 七. 补充:获取类的属性、方法、特性、构造函数等,设置属性值,获取属性值[/b]
函数 说明
GetConstructor(s) 取得此类型的创建函数,其将回传一个ConstructorInfo对象或数组
GetField(s) 取得此类型中成员变量,其将回传一个FiledInfo对象或数组
GetMember(s) 取得此类中的成员,其类型可以是变量、事件、属性、方法及Nested Type,其将回传一个MemberInfo对象或数组
GetEvent(s) 取得此类型中的事件,其将回传一个EventInfo对象或数组
GetProperty/GetProperties 取得此类型中的属性,其将回传一个PropertyInfo对象或数组
GetNestedType(s) 取得声明于此类型内类型,其将回传一个Type对象或数组
GetCustomAttibutes 取得绑定于此类型的Attitudes
GetValue(t) 获取t对象的的属性值
SetValue(t,"XXX") 设置t对象的属性值为XXX
实体代码:
[Description("我是Person类")] public class Person { //1. 构造函数 public Person() { } public Person(string sex) { this._Sex = sex; } //2. 属性 [Description("我是id")] public string id { get; set; } [ReadOnly(true)] public string userName { get; set; } public string userPwd { get; set; } //3.成员变量 public string _Sex = null; public int _Age; //4. 特性 [Description("我是id")] [ReadOnly(true)] //5. 方法 public string GetOwnSex() { return this._Sex; } //6. 事件 public event Action MyEvent; }
调用代码:
Person person1 = new Person() { id = "123", userName = "ypf", userPwd = "123456", }; //获取类 Type type = person1.GetType(); { Console.WriteLine("---------------1. 获取构造函数-----------------"); ConstructorInfo[] constructorInfoList = type.GetConstructors(); for (int i = 0; i < constructorInfoList.Length; i++) { Console.WriteLine(constructorInfoList[i]); } } { Console.WriteLine("---------------2. 获取成员变量-----------------"); FieldInfo[] fielInforList = type.GetFields(); for (int i = 0; i < fielInforList.Length; i++) { Console.WriteLine(fielInforList[i]); } } { Console.WriteLine("---------------3. 获取成员-----------------"); MemberInfo[] memberInfoList = type.GetMembers(); for (int i = 0; i < memberInfoList.Length; i++) { Console.WriteLine(memberInfoList[i]); } } { Console.WriteLine("---------------4. 获取事件-----------------"); EventInfo[] eventInfoList = type.GetEvents(); for (int i = 0; i < eventInfoList.Length; i++) { Console.WriteLine(eventInfoList[i]); } } { Console.WriteLine("---------------5. 获取属性-----------------"); PropertyInfo[] propertyInfoList = type.GetProperties(); for (int i = 0; i < propertyInfoList.Length; i++) { Console.WriteLine(propertyInfoList[i]); } } { Console.WriteLine("---------------6. 获取特性-----------------"); //1. 获取属性上的特性 //因为这些测试所用的特性都是加在属性上的,所以要先获取属性 PropertyInfo[] propertyInfoList = type.GetProperties(); foreach (var item in propertyInfoList) { //获取该属性上的所有特性 object[] attributeInfoList = item.GetCustomAttributes(true); foreach (var item2 in attributeInfoList) { Console.WriteLine("{0}属性上的特性为{1}", item, item2); } } //2. 获取类上的属性 object[] attributeInfoList2 = type.GetCustomAttributes(true); foreach (var item3 in attributeInfoList2) { Console.WriteLine("{0}类上的特性为{1}", type, item3); } } { Console.WriteLine("---------------7. 获取id属性的值-----------------"); PropertyInfo idProperty = type.GetProperty("id"); Console.WriteLine("属性名为:{0}", idProperty.Name); Console.WriteLine("属性值为:{0}", idProperty.GetValue(person1)); //设置属性值 idProperty.SetValue(person1, "2345"); Console.WriteLine("设置后的属性值为:{0}", idProperty.GetValue(person1)); }
结果:
相关文章推荐
- 利用java的反射实现IOC
- Java仿Spring框架IOC控制反转利用反射简单实现(源码)
- 利用Java的反射与代理机制实现IOC
- 利用Java的反射与代理机制实现IOC
- 利用Java的反射实现spring的IOC(推荐!!!)
- Android 学习 利用反射实现Android中的ioc框架
- 利用Java的反射与代理实现IOC模式
- 利用Java的反射与代理实现IOC模式
- 利用Java的反射与代理实现IOC模式
- 利用Java的反射与代理机制实现IOC
- 利用VC+OpenGL实现几种特殊图形效果
- 利用工厂实现IOC
- 利用.NET中的反射机制实现IList到DataTable的转换
- [转]利用.NET中的反射机制实现IList到DataTable的转换
- 利用javascript实现页面跳转的几种方法
- 使用反射实现根据名称动态创建窗体的几种方法
- 利用.NET中的反射机制实现IList到DataTable的转换
- 利用反射实现窗体动态加载
- Javascript利用反射机制和prototype实现类的继承
- 利用VC+OpenGL实现几种特殊图形效果