• 20181112_反射基础_对象获取


    一.   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[] { "孙悟空" });
                    }
    

      

     

     

  • 相关阅读:
    类的组合
    类的派生
    类的继承
    对象的高度整合
    类和数据类型
    对象的绑定方法
    对象属性查找顺序
    C++中struct和class的区别 [转]
    curl_setopt函数相关应用及介绍(转)
    linux 下如何查看和踢除正在登陆的其它用户 ==>Linux下用于查看系统当前登录用户信息的4种方法
  • 原文地址:https://www.cnblogs.com/wxylog/p/9944733.html
Copyright © 2020-2023  润新知