反射

反射

反射反射程序员的快乐,反射是无处不在的,封装框架,系统开发,MVC,IOC,ORM都离不开反射。

一、反射/反编译工具/高级语言到计算机语言的历程

  1. 高级语言---编译---dll/exe文件
  2. metadata:元数据清单---记录了dll中包含哪些东西,是一个描述。
  3. IL:中间语言---编译把高级语言编译后得到c#中最真实的言语状态,面向对象语言。
  4. 反编译工具:逆向工程;ILSPY --- DLL/EXE、 --- 反编译回来,c#/IL
  5. 反射: 来自于System.Reflection,是一个帮助类库---可以读取DLL/EXE中metadata和使用metadata + 动态的创建dll/exe --- Emit技术

二、反射创建对象

首先定义一个接口

namespace Business.DB.Interface
{
    /// <summary>
    /// 数据访问类抽象
    /// </summary>
    public interface IDBHelper
    {
        void Query();
    }
}

实现了接口的类

namespace Business.DB.SqlServer
{
    public class SqlServerHelper
    {
        //Nuget:System.Data.SqlClient

        private string ConnectionString = "Data Source=DESKTOP-VUL99EF; Database=CustomerDB; User ID=sa; Password=sa123; MultipleActiveResultSets=True";

        private static string GetConnection()
        {
            //Nuget引入:
            //SetBasePath:Microsoft.Extensions.Configuration.FileExtensions
            //AddJsonFile:Microsoft.Extensions.Configuration.Json 
            var Configuration = new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile("appsettings.json", optional: true)
                .Build();
            return Configuration.GetConnectionString("Default");
        }


        public SqlServerHelper()
        {
            Console.WriteLine($"{this.GetType().Name}被构造");
        }

        //public SqlServerHelper(int i)
        //{
        //    Console.WriteLine($"{this.GetType().Name}被构造");
        //}

        public void Query()
        {
            Console.WriteLine($"{this.GetType().Name}.Query");
        }
    }
}

我们开始尝试通过反射创建对象

//传统方式创建对象
IDBHelper dbHelper=new SqlServerHelper();

反射的方式

  1. 动态读取DLL
  2. LoadFrom: dll全名称,需要后缀
  3. LoadFile: 全路径,需要DLL后缀
  4. Load: dll名称,不需要后缀
  5. 获取某一个具体的类型:参数需要是类的全名称;
  6. 创建对象
  7. 类型转换
  8. 调用方法
Assembly assembly =Assembly.LoadFrom("Business.DB.SqlServer.dll");
//Assembly assembly1=Assembly.LoadFile(@"E:\VS项目\2022年vs项目\ReLearn\ReLearn\bin\Debug\net7.0\Business.DB.SqlServer.dll");
//Assembly assembly2=Assembly.Load("Business.DB.SqlServer");
Type type= assembly.GetType("Business.DB.SqlServer.SqlServerHelper");
object? oInstance= Activator.CreateInstance(type);

//oInstance.Query(); //报错。因为oInstance当做是一个object类型,object类型是没有Query方法的;
//C#语言是一个强类型语言:编译时你是什么类型,以左边为准;不能调用是因为编译器不允许;实际类型一定是SqlServerHelper类型;

//如果使用dynamic 作为类型的声明,在调用的时候,没有限制;
//dynamic:动态类型;不是编译时决定类型,避开编译器的检查;运行时决定是什么类型
//dynamic oInstance1= Activator.CreateInstance(type);
//oInstance1.Query();
//oInstance1.Get(); //报错了,因为SqlServerHelper没有Get方法。

SqlServerHelper  sqlServerHelper = oInstance as SqlServerHelper;

sqlServerHelper.Query();

三、反射实现工厂,断开依赖

首先我们先使用传统的方式创建一个简单工厂

 public class SimpleFactory
    {
        public static IDBHelper CreateInstance()
        {
            Assembly assembly = Assembly.LoadFrom("Business.DB.SqlServer.dll");
            
            Type type = assembly.GetType("Business.DB.SqlServer.SqlServerHelper");

            object? oInstance = Activator.CreateInstance(type);
            IDBHelper helper = oInstance as IDBHelper;
            return helper;
        }
    }

我们在使用的时候,只需要调用CreateInstance返回一个IDBHelper对象即可。
但是有时候可能会遇到产品经理,让我们使用不同数据库去存储我们的数据的情况,遇到这种情况的时候,可能就需要更改我们的代码来实现不同数据库的切换,因此我们就可以通过反射机制,尽可能的达到一劳永逸的结果。
改造一下我们的简单工厂模式

 public class SimpleFactory
    {
        public static IDBHelper CreateInstance()
        {
            string ReflictionConfig = CustomConfigManager.GetConfig("ReflictionConfig");
            
            string typeName = ReflictionConfig.Split(',')[0];
            string dllName = ReflictionConfig.Split(',')[1];

         
            Assembly assembly = Assembly.LoadFrom(dllName);
            Type type = assembly.GetType(typeName);

            object? oInstance = Activator.CreateInstance(type);
            IDBHelper helper = oInstance as IDBHelper;
            return helper;
        }
    }

    public static class CustomConfigManager
    {
        public static string GetConfig(string key)
        {
            var builder = new ConfigurationBuilder().AddJsonFile("appsettings.json");
            IConfigurationRoot config = builder.Build();
            string configValue = config.GetSection(key).Value;
            return configValue;
        }
    }

创建SqlServerHelper的时候,没有出现SqlserverHelper;没有依赖SqlServerHelper;
依赖的是两个字符串Business.DB.SqlServer.dll +Business.DB.SqlServer.SqlServerHelper
去掉对细节的依赖的;依赖于抽象--不再依赖于细节;----依赖倒置原则; 增强代码的稳定性;

如果我们要切换到MySql服务器

  1. 按照接口实现一个Mysql帮助类库
  2. Copy Dll文件到执行目录下
  3. 修改配置文件

结果:不同功能的灵活切换---程序的配置;
如果一个新的功能还不存在,只需要把功能实现、dll Copy到执行目录下,修改配置文件---不需要停止程序的运行;
扩展一个新的功能进来;

两者的比较

  1. 传统方式,必须修改代码---必须要重新发布 编译 发布到服务器---步骤就很麻烦
  2. 反射实现:断开了对普通类的依赖;依赖于配置文件+接口(抽象)

四、反射黑科技--反射可以做到普通方式做不到的事情;反射破坏单例

先实现一个单例的代码:

 public sealed class Singleton
    {
        private static Singleton instance;
        private Singleton() { }

        static Singleton()
        {
            instance = new Singleton();
        }

        public static Singleton GetInstance() {
            return instance;
        }
    }
  1. 如果私有化修饰--除了反射以外,没有其他的方法来完成对私有化的访问的,私有化就只能从内部访问;
  2. 反射的强大之处---可以做到普通方法做不到的事儿;
  3. 反射动态记载dll,元数据中只要有的,都可以给找出来;
  4. 完全不用关注权限问题---为所欲为---小暗门。

创建一个单例以后,我们对比一下创建对象是否是一致的

      Singleton singleton1 = Singleton.GetInstance();
      Singleton singleton2 = Singleton.GetInstance();
      Singleton singleton3 = Singleton.GetInstance();
      Singleton singleton4 = Singleton.GetInstance();
      Console.WriteLine(object.ReferenceEquals(singleton1, singleton2));
      Console.WriteLine(object.ReferenceEquals(singleton2, singleton3));
      Console.WriteLine(object.ReferenceEquals(singleton1, singleton4)

结果为

下面用反射我们来创建一下我们的对象,

Assembly assembly = Assembly.LoadFrom("Business.DB.SqlServer.dll");
Type type = assembly.GetType("Business.DB.SqlServer.Singleton");
Singleton singleton1 = (Singleton)Activator.CreateInstance(type, true);
Singleton singleton2 = (Singleton)Activator.CreateInstance(type, true);
Singleton singleton3 = (Singleton)Activator.CreateInstance(type, true);
Singleton singleton4 = (Singleton)Activator.CreateInstance(type, true);
Console.WriteLine(object.ReferenceEquals(singleton1, singleton2));
Console.WriteLine(object.ReferenceEquals(singleton2, singleton3));
Console.WriteLine(object.ReferenceEquals(singleton1, singleton4));

这样就达到了通过反射破坏单例的现象。

五、反射调用方法+反射创建对象的升级篇

1.反射创建对象

在通过用反射创建对象的时候,如何调用类的带参数的构造函数那?下面我们演示一下,如何调用带参数的构造函数。
用来反射测试的类

/// <summary>
    /// 反射测试类
    /// </summary>
    public class ReflectionTest
    {
        #region Identity
        /// <summary>
        /// 无参构造函数
        /// </summary>
        public ReflectionTest()
        {
            
            Console.WriteLine($"这里是{this.GetType()}无参数构造函数");
        }

        /// <summary>
        /// 带参数构造函数
        /// </summary>
        /// <param name="name"></param>
        public ReflectionTest(string name)
        {
            Console.WriteLine($"这里是{this.GetType()} 有参数构造函数");
        }

        public ReflectionTest(int id)
        {
            Console.WriteLine($"这里是{this.GetType()} 有参数构造函数");
        }


        public ReflectionTest(int id, string name)
        {

            //typeof(int);

            //Type //id.GetType();

            Console.WriteLine($"这里是{this.GetType()} 有参数构造函数");
        }

        public ReflectionTest(string name,int id )
        {

            //typeof(int);

            //Type //id.GetType();

            Console.WriteLine($"这里是{this.GetType()} 有参数构造函数");
        }

        #endregion

        #region Method
        /// <summary>
        /// 无参方法
        /// </summary>
        public void Show1()
        {
            Console.WriteLine($"这里是{this.GetType()}的Show1" );
        }
        /// <summary>
        /// 有参数方法
        /// </summary>
        /// <param name="id"></param>
        public void Show2(int id)
        {

            Console.WriteLine($"这里是{this.GetType()}的Show2");
        }
        /// <summary>
        /// 重载方法之一
        /// </summary>
        /// <param name="id"></param>
        /// <param name="name"></param>
        public void Show3(int id, string name)
        {
            Console.WriteLine($"这里是{this.GetType()}的Show3");
        }
        /// <summary>
        /// 重载方法之二
        /// </summary>
        /// <param name="name"></param>
        /// <param name="id"></param>
        public void Show3(string name, int id)
        {
            Console.WriteLine($"这里是{this.GetType()}的Show3_2");
        }
        /// <summary>
        /// 重载方法之三
        /// </summary>
        /// <param name="id"></param>
        public void Show3(int id)
        {

            Console.WriteLine($"这里是{this.GetType()}的Show3_3");
        }
        /// <summary>
        /// 重载方法之四
        /// </summary>
        /// <param name="name"></param>
        public void Show3(string name)
        {

            Console.WriteLine("这里是{this.GetType()}的Show3_4");
        }
        /// <summary>
        /// 重载方法之五
        /// </summary>
        public void Show3()
        {

            Console.WriteLine($"这里是{this.GetType()}的Show3_1");
        }
        /// <summary>
        /// 私有方法
        /// </summary>
        /// <param name="name"></param>
        private void Show4(string name)  //肯定是可以的
        {
            Console.WriteLine($"这里是{this.GetType()}的Show4");
        }
        /// <summary>
        /// 静态方法
        /// </summary>
        /// <param name="name"></param>
        public static void Show5(string name)
        {
            Console.WriteLine($"这里是{typeof(ReflectionTest)}的Show5");
        }
        #endregion
    }

反射的代码

 //Type type = null;
                        //Activator.CreateInstance();//访问的是无参数构造函数创建对象;
                        //如何访问带参数的构造函数呢? 
                        Assembly assembly = Assembly.LoadFrom("Business.DB.SqlServer.dll");
                        Type type = assembly.GetType("Business.DB.SqlServer.ReflectionTest");
                        //a. 调用无参数构造函数的
                        object noParaObject = Activator.CreateInstance(type);
                        //b. 调用有参数的---需要传递一个object类型的数组作为参数,参数按照从昨往右匹配;严格匹配,按照参数的类型去执行对应的和参数类型匹配的构造函数,如果没有匹配的---报异常
                        object paraObject = Activator.CreateInstance(type, new object[] { 123 });
                        object paraObject1 = Activator.CreateInstance(type, new object[] { "纯洁的逗比" });
                        object paraObject2 = Activator.CreateInstance(type, new object[] { 234, "丸印" });
                        object paraObject3 = Activator.CreateInstance(type, new object[] { "Richard", 456 });

2. 反射调用参数方法

反射调用方法一定要类型转换后才能调用吗? ---当然不是的;

  1. 获取方法MethodInfo
  2. 执行MethodInfo 的Invoke方法,传递方法所在的类的实例对象+参数

对于不同的类型的函数
a. 调用五参数的方法

Assembly assembly = Assembly.LoadFrom("Business.DB.SqlServer.dll");
Type type = assembly.GetType("Business.DB.SqlServer.ReflectionTest");
object oInstance = Activator.CreateInstance(type); 
MethodInfo show1 = type.GetMethod("Show1");
show1.Invoke(oInstance, new object[] { });
show1.Invoke(oInstance, new object[0]);
show1.Invoke(oInstance, null); 

b. 调用有参数的方法---重载方法--需要通过方法参数类型类区别方法,传递参数--严格匹配参数类型

 Assembly assembly = Assembly.LoadFrom("Business.DB.SqlServer.dll");
 Type type = assembly.GetType("Business.DB.SqlServer.ReflectionTest");
 object oInstance = Activator.CreateInstance(type);
 MethodInfo show2 = type.GetMethod("Show2");
 show2.Invoke(oInstance, new object[] { 123 });

 MethodInfo show31 = type.GetMethod("Show3", new Type[] { typeof(string), typeof(int) });
 show31.Invoke(oInstance, new object[] { "细雨浮萍", 234 });

 MethodInfo show32 = type.GetMethod("Show3", new Type[] { typeof(int) });
 show32.Invoke(oInstance, new object[] { 345 });


 MethodInfo show33 = type.GetMethod("Show3", new Type[] { typeof(string) });
 show33.Invoke(oInstance, new object[] { "幸福靓装" });

 MethodInfo show34 = type.GetMethod("Show3", new Type[0]);
 show34.Invoke(oInstance, null);

c. 调用私有方法,在获取方法的时候,加上参数BindingFlags.NonPublic | BindingFlags.Instance

Assembly assembly = Assembly.LoadFrom("Business.DB.SqlServer.dll");
Type type = assembly.GetType("Business.DB.SqlServer.ReflectionTest");
object oInstance = Activator.CreateInstance(type);
MethodInfo show4 = type.GetMethod("Show4", BindingFlags.NonPublic | BindingFlags.Instance);
show4.Invoke(oInstance, new object[] { "String" });

d. 调用泛型方法: 1. 获取到方法后,先确定类型,严格按照参数类型传递参数就可以正常调用
泛型是延迟声明,调用的时候,确定类型

{
    //GenericMethod genericMethod = new GenericMethod();
    //genericMethod.Show<string, int, DateTime>("", 23, DateTime.Now);
}
{
    //Assembly assembly = Assembly.LoadFrom("Business.DB.SqlServer.dll");
    //Type type = assembly.GetType("Business.DB.SqlServer.GenericMethod");
    //object oInstance = Activator.CreateInstance(type);
    //MethodInfo show = type.GetMethod("Show");
    ////在执行之前选哟确定是什么类型; 
    //MethodInfo genericshow = show.MakeGenericMethod(new Type[] { typeof(int), typeof(string), typeof(Dat
    //genericshow.Invoke(oInstance, new object[] { 123, "暖风昔人", DateTime.Now });
    //genericshow.Invoke(oInstance, new object[] { "暖风昔人", 123, DateTime.Now });

    ////show.Invoke(); //到到底给什么类型呢? 泛型,其实不确定的类型的;
}
{

    //Console.WriteLine(typeof(GenericClass<,,>)); 
    //Assembly assembly = Assembly.LoadFrom("Business.DB.SqlServer.dll");
    //Type type = assembly.GetType("Business.DB.SqlServer.GenericClass`3");
    //Type generType = type.MakeGenericType(new Type[] { typeof(int), typeof(string), typeof(DateTime) });
    //object oInstance = Activator.CreateInstance(generType); 
    //MethodInfo show = generType.GetMethod("Show");
    //show.Invoke(oInstance,new object[] { 123, "赤", DateTime.Now });
}
{
    //Assembly assembly = Assembly.LoadFrom("Business.DB.SqlServer.dll");
    //Type type = assembly.GetType("Business.DB.SqlServer.GenericDouble`1");
    //Type generType = type.MakeGenericType(new Type[] { typeof(int) });
    //object oInstance = Activator.CreateInstance(generType);
    //MethodInfo show = generType.GetMethod("Show");

    //MethodInfo genericMethod = show.MakeGenericMethod(new Type[] { typeof(string), typeof(DateTime) }); 
    //genericMethod.Invoke(oInstance, new object[] { 123, "鱼儿", DateTime.Now });
}

六、反射多种应用场景

1.反射+ 配置文件+ 工厂-----后面要讲的IOC容器---IOC容器的雏形;简单版本的IOC容器;
2.调用方法--需要类型名称+ 方法名称就可以调用到方法 ---类类名称(字符串)+ 方法名称(字符串) ===就可以去调用方法--MVC框架
创建一个MVC ---http://localhost:64527/Home/Index 就可以调用到Index方法---就选需要创建Home的实例;执行Index方法---当然是反射啊;

七、 反射的局限性

  1. 有局限----性能问题
  2. 测试用例:普通方式循环100000次创建对象+方法调用:17 ms
    反射方式循环100000此创建对象+方法调用:4544ms
    代码
 public class Monitor
    {
        public static void Show()
        {
            Console.WriteLine("*******************Monitor*******************");
            long commonTime = 0;
            long reflectionTime = 0;
            {
                Stopwatch watch = new Stopwatch();
                watch.Start();
                for (int i = 0; i < 1000_000; i++) //1000000000
                {
                    IDBHelper iDBHelper = new SqlServerHelper();
                    iDBHelper.Query();
                }
                watch.Stop();
                commonTime = watch.ElapsedMilliseconds;
            }
            {
                Stopwatch watch = new Stopwatch();
                watch.Start();
                //Assembly assembly = Assembly.LoadFrom("Business.DB.SqlServer.dll");//1 动态加载
                //Type dbHelperType = assembly.GetType("Business.DB.SqlServer.SqlServerHelper");//2 获取类型  
                for (int i = 0; i < 1000_000; i++)
                {
                    Assembly assembly = Assembly.LoadFrom("Business.DB.SqlServer.dll");//1 动态加载
                    Type dbHelperType = assembly.GetType("Business.DB.SqlServer.SqlServerHelper");//2 获取类型  
                    object oDBHelper = Activator.CreateInstance(dbHelperType);//3 创建对象
                    IDBHelper dbHelper = (IDBHelper)oDBHelper;//4 接口强制转换
                    dbHelper.Query();//5 方法调用
                }
                watch.Stop();
                reflectionTime = watch.ElapsedMilliseconds;
            }

            Console.WriteLine($"commonTime={commonTime} reflectionTime={reflectionTime}");
        }
    }

运行的结果为:

  1. 第二次运行: 普通方式:15ms 反射方式:4517ms
    确实有性能问题,主要损耗性能是加载DLL的时候损耗的,
    怎么优化一下?
    把动态加载Dll部分缓存起来了;每次直接使用加载的dll;
Stopwatch watch = new Stopwatch();
watch.Start();
Assembly assembly = Assembly.LoadFrom("Business.DB.SqlServer.dll");//1 动态加载
Type dbHelperType = assembly.GetType("Business.DB.SqlServer.SqlServerHelper");//2 获取类型
for (int i = 0; i < 1000_000; i++)
{

    object oDBHelper = Activator.CreateInstance(dbHelperType);//3 创建对象
    IDBHelper dbHelper = (IDBHelper)oDBHelper;//4 接口强制转换
    dbHelper.Query();//5 方法调用
}
watch.Stop();
reflectionTime = watch.ElapsedMilliseconds;

使用反射的建议:

  1. 反射确实有性能问题
  2. 需要理性对待,就不用吗?当然要用;因为反射的经过优化之后其实性能差也没有差多少的;因为反射能量确实是太大了,大家以后在工作中,可以放心使用;

八、反射调用字段

一个类的内部,除了构造函数、方法、属性、字段。

 public class People
    {
        public People()
        {
            Console.WriteLine("{0}被创建", this.GetType().FullName);
        } 
        public int Id { get ; set; } //带有Get  Set 方法的叫做属性
         
        public string Name { get; set; }

        public int Age { get; set; }

        public int Sex { get; set; }

        public string Description;  //字段           
    }

传统方式使用属性和字段

 //传统方式使用属性和字段
                    {
                        Console.WriteLine("***********赋值*************");
                        People people = new People();
                        people.Id = 134;
                        people.Name = "陈大宝";
                        people.Age = 25;
                        people.Description = "高级班的VIP学员";
                        people.Sex = 1;
                        People people1 = new People()
                        {
                            Id = 134,
                            Name = "陈大宝",
                            Age = 25,
                            Description = "高级班的VIP学员"
                        };
                        Console.WriteLine("***********取值*************");
                        Console.WriteLine($"people.Id={people.Id}");
                        Console.WriteLine($"people.Name={people.Name}");
                        Console.WriteLine($"people.Age={people.Age}");
                        Console.WriteLine($"people.Sex={people.Sex}");
                        Console.WriteLine($"people.Description={people.Description}");
                    }

反射方式

Console.WriteLine("***********反射方式*************");
                    {
                        Type type = typeof(People);
                        object pObject = Activator.CreateInstance(type);
                        //pObject.Id=234; 
                        foreach (var prop in type.GetProperties())
                        {
                            //Console.WriteLine(prop.Name);
                            //逐个属性赋值
                            if (prop.Name.Equals("Id"))
                            {
                                prop.SetValue(pObject, 134);
                            }
                            else if (prop.Name.Equals("Name"))
                            {
                                prop.SetValue(pObject, "陈大宝");
                            }
                            else if (prop.Name.Equals("Age"))
                            {
                                prop.SetValue(pObject, 25);
                            }
                            //else if (prop.Name.Equals("Age"))
                            //{
                            //    prop.SetValue(pObject, 25);
                            //}
                        }                      
                        foreach (var prop in type.GetProperties())
                        {
                            Console.WriteLine($"people.{prop.Name}={prop.GetValue(pObject)}");

                            //if (prop.Name.Equals("Id"))
                            //{
                            //    prop.GetValue(pObject);
                            //}
                            //else if (prop.Name.Equals("Name"))
                            //{
                            //    prop.GetValue(pObject);
                            //}
                            //else if (prop.Name.Equals("Age"))
                            //{
                            //    prop.GetValue(pObject);
                            //   }
                        }
                    }

总结:

  1. 普通方法调用属性字段---简单快捷
  2. 反射操作---稍微麻烦点
  3. 让我们的程序更加稳定---
  4. 如果说People增加一个字段呢?增加一个属性呢?--普通方法就必须要修改代码---重新编译发布----不稳定
  5. 反射设置值:好像没啥优势
  6. 反射取值:不需要修改代码---代码就更加稳定

九、反射+ADO.NET时间数据库访问层---手写ORM框架

  1. ORM--对象关系映射
  2. 可以通过依赖于某个实体对象--做到对数据库中数据的操作
 public class SqlServerHelper : IDBHelper
    {

        //Nuget:System.Data.SqlClient

        private string ConnectionString = "Data Source=10.10.33.251; Database=ZhaoXiPracticeDB; User ID=sa; Password=7ujm&UJM; MultipleActiveResultSets=True";

        private static string GetConnection()
        {
            //Nuget引入:
            //SetBasePath:Microsoft.Extensions.Configuration.FileExtensions
            //AddJsonFile:Microsoft.Extensions.Configuration.Json 
            var Configuration = new ConfigurationBuilder()
             .SetBasePath(Directory.GetCurrentDirectory())
             .AddJsonFile("appsettings.json", optional: true)
             .Build();
            return Configuration.GetConnectionString("Default");
        }


        public SqlServerHelper()
        {
            //Console.WriteLine($"{this.GetType().Name}被构造");
        }

        //public SqlServerHelper(int i)
        //{
        //    Console.WriteLine($"{this.GetType().Name}被构造");
        //}

        public void Query()
        {
            //Console.WriteLine($"{this.GetType().Name}.Query");
        }

        /// <summary>
        /// 查询Company
        /// 条件:
        /// 1.必须有一个Id字段--继承BaseModel
        /// 2.要求实体对象中的属性结构必须和数据完全一致
        /// </summary>
        public SysCompany QuerySysCompany(int id)
        {
            //开始ORM
            //1.链接数据---数据库链接字符串ConnectionString 
            //2.准备SqlConnection,使用数据库链接字符串

            //SysCompany result = new SysCompany();
            //7.反射创建对象oReulst--- 给oReulst 赋值然后返回  oReulst
            Type type = typeof(SysCompany);
            object oReulst = Activator.CreateInstance(type);

            using (SqlConnection connection = new SqlConnection(ConnectionString))
            {
                connection.Open();//打开数据库链接
                //3.准时Sql语句
                #region MyRegion
                string sql = @"SELECT [Id]
                                  ,[Name]
                                  ,[CreateTime]
                                  ,[CreatorId]
                                  ,[LastModifierId]
                                  ,[LastModifyTime]
                                    FROM [ZhaoXiPracticeDB].[dbo].[SysCompany] where  id=" + id;
                #endregion 
                //4.准备SqlCommand
                SqlCommand sqlCommand = new SqlCommand(sql, connection);
                //5.通过SqlCommand对象执行Sql语句
                SqlDataReader reader = sqlCommand.ExecuteReader();
                //6.开始获取数据

                if (reader.Read())
                {
                    //object oId = reader["Id"];
                    //result.Id = Convert.ToInt32(oId);//不行;
                    //object oName = reader["Name"]; 
                    //8.反射赋值?
                    foreach (var prop in type.GetProperties())
                    {
                        //if (prop.Name.Equals("Id"))
                        //{
                        //    prop.SetValue(oReulst, reader["Id"]); //Id
                        //}
                        //else if (prop.Name.Equals("Name"))
                        //{
                        //    prop.SetValue(oReulst, reader[prop.Name]);
                        //}
                        //else if (prop.Name.Equals("CreateTime"))
                        //{
                        //    prop.SetValue(oReulst, reader[prop.Name);
                        //}

                        //else if (true)
                        //{
                        //.......
                        //}

                        Console.WriteLine(prop.GetValue(oReulst, null));
                       // Console.WriteLine(prop.Name);
                       
                  

                    }
                }
                //问题来了,数据可以查询到---需要返回一个对象--需要赋值给对象,然后返回对象;----反射 
            }
            return (SysCompany)oReulst;
        }
}

十、Emit技术:在运行时去动态的生成Dll、Exe包括dll内部的方法、属性、字段

  1. 动态--;可以在代码运行的时候去动态的定义业务逻辑;
  2. 偏向于底层--学习成本比较高 除非是非常复杂的业务逻辑,一般情况,用的比较少---动态代理的时候,还会再讲的;
    ReflectionEmit.cs
public class ReflectionEmit
    {
        public static void Show()
        {

            // AssemblyBuilder// 建造者模式---创建Assembly

            //AssemblyName assemblyName = new AssemblyName("DynamicAssemblyExample");

            AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("DynamicAssemblyExample"), AssemblyBuilderAccess.RunAndCollect);

            // 对于单个模块程序集,模块名称通常为;程序集名称加上扩展名。  
            ModuleBuilder modulebuilder = assemblyBuilder.DefineDynamicModule("MyModal"); //托管模块
            TypeBuilder typebuilder = modulebuilder.DefineType("MyDynamicType", TypeAttributes.Public);

            //Type type= typebuilder.CreateType();

            // 在Type中生成私有字段
            FieldBuilder fieldBuilder = typebuilder.DefineField("NumberField", typeof(int), FieldAttributes.Public);

            // 定义一个接受整数参数的构造函数,储存在私人区域。  
            Type[] parameterTypes = { typeof(int) };
            ConstructorBuilder ctor1 = typebuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, parameterTypes);

            //中间语言的生成者
            ILGenerator ctor1IL = ctor1.GetILGenerator();

            //对于构造函数,参数0是对新 
            //实例。在调用base之前将其推到堆栈上 
            //类构造函数。指定的默认构造函数 
            //通过传递 
            //类型(Type.EmptyTypes)到GetConstructor。 

            ctor1IL.Emit(OpCodes.Ldarg_0);
            ctor1IL.Emit(OpCodes.Call, typeof(object).GetConstructor(Type.EmptyTypes));

            //在推送参数之前,先将实例推送到堆栈上 
            //将被分配给私有字段m\u编号。 
            ctor1IL.Emit(OpCodes.Ldarg_0);
            ctor1IL.Emit(OpCodes.Ldarg_1);
            ctor1IL.Emit(OpCodes.Stfld, fieldBuilder);
            ctor1IL.Emit(OpCodes.Ret);

            //完成构造函数传值, Int 设置给字段--测试代码
            {
                //MyDynamicType myDynamicType = new MyDynamicType(123456);
                //int number = myDynamicType.NumberField;

                //// 动态的生成程序集;
                //// 动态的生成类;
                //// 动态的生成字段;
                //// 动态的生成构造函数;
                //Type type1 = typebuilder.CreateType();
                //object oInstacne = Activator.CreateInstance(type1, new object[] { 123456 });
                //FieldInfo fieldInfo = type1.GetField("NumberField");
                //object numberFieldResult = fieldInfo.GetValue(oInstacne);
            }

            MethodBuilder consoleMethod = typebuilder.DefineMethod("ConsoleMethod", MethodAttributes.Public | MethodAttributes.Static, null, null);

            ILGenerator consoleMethodIL = consoleMethod.GetILGenerator();
            consoleMethodIL.Emit(OpCodes.Ldstr, "欢迎来到高级班第15期进阶学习");
            consoleMethodIL.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }));
            consoleMethodIL.Emit(OpCodes.Ret); //写IL最后一定要Ret



            {
                Console.WriteLine("测试。。。。。。");
                Type type1 = typebuilder.CreateType();
                object oInstacne = Activator.CreateInstance(type1, new object[] { 123456 });
                MethodInfo myMethod = type1.GetMethod("ConsoleMethod");
                object oResult = myMethod.Invoke(oInstacne, null);

            } 
            MethodBuilder AddMethod = typebuilder.DefineMethod("AddMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(int), new Type[] { typeof(int), typeof(int) });
            ILGenerator AddMethodIL = AddMethod.GetILGenerator();
            AddMethodIL.Emit(OpCodes.Ldarg_0);
            AddMethodIL.Emit(OpCodes.Ldarg_1);
            AddMethodIL.Emit(OpCodes.Add_Ovf_Un);
            AddMethodIL.Emit(OpCodes.Ret);
            {
                //Console.WriteLine("测试。。。。。。");
                //Type type1 = typebuilder.CreateType();
                //object oInstacne = Activator.CreateInstance(type1, new object[] { 123456 });
                //MethodInfo myMethod = type1.GetMethod("AddMethod");
                //object oResult = myMethod.Invoke(oInstacne, new object[] { 12, 34 }); 
            }
        }
    }

main.cs

Assembly assembly = Assembly.LoadFrom("Business.DB.SqlServer.dll");
                    Module[] modules = assembly.GetModules();
                    foreach (var module in modules)
                    {
                        Console.WriteLine(module.Name);
                        foreach (var type in module.GetTypes())
                        {
                            Console.WriteLine(type.Name);
                        }
                    }
                    ReflectionEmit.Show();

posted @ 2023-04-05 18:02  飘雨的河  阅读(47)  评论(0编辑  收藏  举报