20181112_反射基础_对象获取

Posted wxylog

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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[] { "孙悟空" });
                }

  

 

 

 

以上是关于20181112_反射基础_对象获取的主要内容,如果未能解决你的问题,请参考以下文章

Java__反射基础复习

Java__反射基础复习

面向对象基础 反射

面向对象基础 反射

python基础学习-面向对象高级

Golang基础_10-反射reflection