一. C#中的反射
什么是反射:在C#中, 反射是有System.Reflection命名空间下的, 由.Net框架提供帮助类库,可以读取并使用dll(或exe)文件的metadata(元数据)
二. 反射的基本写法, 介绍 Load / LoadFile / LoadFrom:
Assembly assembly = Assembly.Load("MyDB.MySql"); //通常推荐使用这一种 //第一步: 从 [当前目录] 加载指定名称的dll;dll名称无后缀 //loadfile 从 指定目录 加载指定名称的dll; dll名称必须带上后缀 Assembly assembly_1 = Assembly.LoadFile(@"C:CsharpProject\_ReflectioninDebugMyDB.MySql.dll"); //LoadFile表示完整路径的加载, 所以可以是别的目录; 如果没有依赖项, 加载的时候也不会报错, 但是在使用的时候会错; 比如加载的是c:windowsA.dll; 但是A内部依赖着B.dll; 此时如果没有同时加载B.dll, 那么在使用的时候就会报错 //Assembly assembly2 = Assembly.LoadFrom("Ruanmou.DB.MySql.dll");//使用LoadFrom加载的时候, 需要带后缀(dll/exe)或者 是文件的完整路径 //无论是LoadFile还是LoadFrom; 底层都是调用的Load() //但是还是有区别, LoadFile不会在加载的时候, 去调用程序集的构造函数; 但是Load会的; foreach (var item in assembly.GetModules()) { //输出模块信息; 这个GetModules里面就只有程序集的名称, 这里面有用的东西也就这个了 Console.WriteLine(item.FullyQualifiedName); } //assembly.GetTypes() 输出本程序集中, 所有类的类名信息; 包含命名空间的名称 foreach (var item in assembly.GetTypes()) { //输出类型信息 Console.WriteLine(item.FullName); } //assembly.GetType→获取指定的某个类的信息; 参数为类的完整名称新包含程序集的名称; //assembly.GetTypes(); 获取所有类的信息; 返回一个type数组 Type type = assembly.GetType("MyDB.MySql.MySqlHelper");//第二步: 获取指定类型的信息 object oDBHelper = Activator.CreateInstance(type);//第三步: 通过反射创建对象 //oDBHelper.Query(); oDBHelper是objec不能调用, 而实际上方法是有的, 但是编译器不认可; 所以必须要类型转换 IDbHelper IDbHelper = (IDbHelper)oDBHelper;//第四步: 类型转换 IDbHelper.Query();//第五步: 方法调用
三. 反射的简单应用, 使用工厂+配置文件+反射 动态创建不同对象实例
a) 配置文件内容:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> </startup> <appSettings> <!--MyDB.MySql.MySqlHelper → 类名称--> <!--MyDB.MySql → dll名称--> <add key="IDbHelperConfig" value="MyDB.MySql.MySqlHelper,MyDB.MySql"/> </appSettings> <connectionStrings> <!--数据库连接字符串--> <add name="Customers" connectionString="Data Source=.; Database=OA; User ID=sa; Password=123456; MultipleActiveResultSets=True" providerName="System.Data.SqlClient" /> </connectionStrings> </configuration>
b) 工厂类代码:
public class Factory { //使用工厂来创建对象, 完成对象创建的封装 ; 由于工厂中使用配置文件, 那么只需要再配置文件中换下配置字符串, 则就能使用不同的类, 代码一行都不需要改动; 同时还可以添加一个原本不存在的DLL, 但是要把需要的DLL复制到当前目录下 /* <appSettings> <!--MyDB.MySql.MySqlHelper → 类名称--> <!--MyDB.MySql → dll名称--> <add key="IDbHelperConfig" value="MyDB.MySql.MySqlHelper,MyDB.MySql"/> </appSettings> */ /// <summary> /// 从配置文件中, 获取程序集的路径 /// </summary> private static string IDBHelperConfig = System.Configuration.ConfigurationManager.AppSettings["IDbHelperConfig"]; /// <summary> /// dll的名称, 不包含后缀; /// </summary> private static string DllName = IDBHelperConfig.Split(',')[1]; /// <summary> /// 类的名称; 用来从之前程序集中取出类名; 类名必须包含命名空间 /// </summary> private static string TypeName = IDBHelperConfig.Split(',')[0]; public static IDbHelper CreateHelper()//1 2 { Assembly assembly = Assembly.Load(DllName);//1 加载dll Type type = assembly.GetType(TypeName);//2 获取类型信息 object oDBHelper = Activator.CreateInstance(type);//3 创建对象 IDbHelper iDBHelper = (IDbHelper)oDBHelper;//4 类型转换 return iDBHelper; } }
c) 调用:
IDbHelper iDbHeler = Factory.CreateHelper();// 使用工厂创建对象 iDbHeler.Query();//能够做到可配置可扩展的原因是: 反射是动态的, 它依赖的是字符串, 而字符串可以任何更改
d) 测试:
看下下面的两个图, 然后做修改:
将右图的value值修改为
MyDB.SqlServer.SqlServerHelper,MyDB.SqlServer
再次运行程序, 发现就是调用的SqlServer来处理数据层
四. 使用反射调用单例类的构造函数, 以其来破坏单例:
a) 单例类的代码如下:
public sealed class Singleton { private static Singleton _Singleton = null; private Singleton() { Console.WriteLine("Singleton被构造"); } static Singleton() { _Singleton = new Singleton(); } public static Singleton GetInstance() { return _Singleton; } }
b) 直接使用单例类, 程序中只能被实例化一次:
//单例模式, 保证程序启动起来后, 这个类只能有一个实例; 下面代码只是测试 Singleton singleton1 = Singleton.GetInstance(); Singleton singleton2 = Singleton.GetInstance(); Singleton singleton3 = Singleton.GetInstance();
c) 使用反射调用单例的构造函数, 以其来创建多个不同实例
//反射破坏单例,主要就是调用单例的构造函数; 注意: 不管是反射还是什么技术, 只要能够多次调用它的构造函数, 就肯定能破坏单例 Assembly assembly = Assembly.Load("MyDB.SqlServer"); Type type = assembly.GetType("MyDB.SqlServer.Singleton"); //使用Singleton强制类型转换 //使用反射可以调用单例模式下类的私有构造函数; 如果想调用单例下的类进行反射则必须传递第二个参数为true ; Singleton singleton4 = (Singleton)Activator.CreateInstance(type ,true); Singleton singleton5 = (Singleton)Activator.CreateInstance(type,true ); Singleton singleton6 = (Singleton)Activator.CreateInstance(type, true);
五. 使用反射调用类的指定构造函数
a) 调用代码:
//调用有参数的构造函数进行反射; 使用new object[]{} Assembly assembly = Assembly.Load("MyDB.SqlServer"); //加载测试类的dll文件 Type type = assembly.GetType("MyDB.SqlServer.ReflectionTest"); //默认调用无参构造函数 object oReflectionTest1 = Activator.CreateInstance(type); //传递参数到构造函数, 构造函数类型/个数, 必须匹配 object oReflectionTest2 = Activator.CreateInstance(type, new object[] {123}); object oReflectionTest3 = Activator.CreateInstance(type, new object[] {"123"});
b) ReflectionTest类的代码如下:
/// <summary> /// 反射测试类; 用来演示如何使用反射调用带参数的构造函数 /// </summary> public class ReflectionTest { #region Identity /// <summary> /// 无参构造函数 /// </summary> public ReflectionTest() { Console.WriteLine("这里是{0}无参数构造函数", this.GetType()); } /// <summary> /// 带参数构造函数string类型 /// </summary> /// <param name="name"></param> public ReflectionTest(string name) { Console.WriteLine("这里是{0} 有参数构造函数", this.GetType()); } /// <summary> /// 带参数构造函数的int类型重载 /// </summary> /// <param name="id"></param> public ReflectionTest(int id) { Console.WriteLine("这里是{0} 有参数构造函数", this.GetType()); } #endregion #region Method /// <summary> /// 无参方法 /// </summary> public void Show1() { Console.WriteLine("这里是{0}的Show1", this.GetType()); } /// <summary> /// 有参数方法 /// </summary> /// <param name="id"></param> public void Show2(int id) { Console.WriteLine("这里是{0}的Show2", this.GetType()); } /// <summary> /// 重载方法之一 /// </summary> /// <param name="id"></param> /// <param name="name"></param> public void Show3(int id, string name) { Console.WriteLine("这里是{0}的Show3", this.GetType()); } /// <summary> /// 重载方法之二 /// </summary> /// <param name="name"></param> /// <param name="id"></param> public void Show3(string name, int id) { Console.WriteLine("这里是{0}的Show3_2", this.GetType()); } /// <summary> /// 重载方法之三 /// </summary> /// <param name="id"></param> public void Show3(int id) { Console.WriteLine("这里是{0}的Show3_3", this.GetType()); } /// <summary> /// 重载方法之四 /// </summary> /// <param name="name"></param> public void Show3(string name) { Console.WriteLine("这里是{0}的Show3_4", this.GetType()); } /// <summary> /// 重载方法之五 /// </summary> public void Show3() { Console.WriteLine("这里是{0}的Show3_1", this.GetType()); } /// <summary> /// 私有方法 /// </summary> /// <param name="name"></param> private void Show4(string name) { Console.WriteLine("这里是{0}的Show4", this.GetType()); } /// <summary> /// 静态方法 /// </summary> /// <param name="name"></param> public static void Show5(string name) { Console.WriteLine("这里是{0}的Show5", typeof(ReflectionTest)); } #endregion }
六. 使用反射获取类中的各个方法; 重载/静态/私有/泛型/父类
Assembly assembly1 = Assembly.Load("MyDB.SqlServer"); Type type1 = assembly1.GetType("MyDB.SqlServer.ReflectionTest"); //type1.BaseType //表示获取他的父类型 object oReflectionTest = Activator.CreateInstance(type1); //使用GetMethods获取该类下的所有方法 foreach (var item in type1.GetMethods()) { //获取该类下所有的方法名称 Console.WriteLine(item.Name); } //oReflectionTest.Show1(); { //通过反射, 使用方法名称类获取方法; MethodInfo method = type1.GetMethod("Show1"); //第一个参数是实例, 第二个参数是 参数列表 method.Invoke(oReflectionTest, null); //null 表示此方法无参数 } { //传递方法的名称 MethodInfo method = type1.GetMethod("Show2"); method.Invoke(oReflectionTest, new object[] { 123 }); } { //静态方法的调用 静态方法的两种调用方式都可以; 可以给实例, 也可以不给实例 MethodInfo method = type1.GetMethod("Show5"); method.Invoke(oReflectionTest, new object[] { "孙悟空" }); //注意调用静态方法是不需要实例化的, 只需要把参数传递过去就行了; 但是普通方法就不行 method.Invoke(null, new object[] { "八戒" }); } { //注意如果调用有多个重载的方法, 那么通过方法名调用的时候, 必定会报错, 因为它能找到多个, 所以必须要连同参数列表一起传递 MethodInfo method = type1.GetMethod("Show3", new Type[] { }); method.Invoke(oReflectionTest, new object[] { }); } { MethodInfo method = type1.GetMethod("Show3", new Type[] { typeof(int) }); //调用int类型的 method.Invoke(oReflectionTest, new object[] { 123 }); } { //调用string 类型的 MethodInfo method = type1.GetMethod("Show3", new Type[] { typeof(string) }); method.Invoke(oReflectionTest, new object[] { "Ant" }); } { MethodInfo method = type1.GetMethod("Show3", new Type[] { typeof(int), typeof(string) }); method.Invoke(oReflectionTest, new object[] { 234, "W" }); } { MethodInfo method = type1.GetMethod("Show3", new Type[] { typeof(string), typeof(int) }); method.Invoke(oReflectionTest, new object[] { "W", 234 }); } { //如何调用私有方法 //注意Show4是私有方法 使用反射调用私有方法 MethodInfo method = type1.GetMethod("Show4", BindingFlags.Instance | BindingFlags.NonPublic); method.Invoke(oReflectionTest, new object[] { "孙悟空" }); }