C#反射详解

一、反射是什么

1、C#编译运行过程

高级语言->编译->dll/exe文件->CLR/JIT->机器码
C#反射详解_第1张图片

2、原理解析

metadata:元数据数据清单,记录了dll中包含了哪些东西,是一个描述。
IL:中间语言,编译把高级语言编译后得到的C#中最真实的语言状态,面向对象语言。

反射:来自于System.Reflection,是一个帮助类库,可以读取dll/exe中metadata,使用metadata创建对象。

Emit:一种反射技术,可以动态创建dll/exe。

反编译工具:ILSpy可以反编译dll/exe,查看对应的C#/IL代码。

二、反射创建对象

1、动态读取dll

  • LoadFrom:dll全名称,需要后缀
  • LoadFile:全路径,需要dll后缀
  • Load:dll名称不需要后缀
//1、动态读取dll的三种方式
//(1)LoadFrom:dll全名称,需要后缀                        
Assembly assembly = Assembly.LoadFrom("Business.DB.SqlServer.dll");
//(2)LoadFile:全路径,需要dll后缀
//Assembly assembly1 = Assembly.LoadFile(@"dll文件全路径");
//(3)Load:dll名称 不需要后缀
//Assembly assembly2 = Assembly.Load("Business.DB.SqlServer");

获取类型

//2、获取某一个具体的类型,参数需要是类的全名称
Type type1 = assembly.GetType("Business.DB.SqlServer.SqlServerHelper");

3、创建对象

  • 直接传类型
  • 重载方法,传dll的全名称
  • 返回值是object类型,不能直接调用方法
//3、创建对象
//(1)直接传类型
object? oInstance = Activator.CreateInstance(type1);
//(2)重载方法,传dll的全名称
//object? oInstanc1= Activator.CreateInstance("Business.DB.SqlServer.dll", "Business.DB.SqlServer.SqlServerHelper");
//a.oInstance.Query();//报错了:因为oInstance当做是一个object类型,object类型是没有Query方法;C#语言是一种强类型语言;编译时决定你是什么类型,以左边为准;不能调用是因为编译器不允许;实际类型一定是SqlServerHelper;
//b.如果使用dynamic 作为类型的声明,在调用的时候,没有限制;
//c.dynamic :动态类型:不是编译时决定类型,避开编译器的检查;运行时决定是什么类型
//d.dynamic dInstance = Activator.CreateInstance(type);
//e.dInstance.Query();
//f.dInstance.Get(); //报错了--因为SqlServerHelper没有Get方法

4、类型转换

//4、类型转换
// SqlServerHelper helper = (SqlServerHelper)oInstance; //不建议这样转换--如果真实类型不一致--会报报错; 
IDBHelper helper = oInstance as IDBHelper;//如果类型一直,就转换,如果不一致;就返回null

5、调用方法

//5、调用方法
helper.Query();

三、反射创建对象封装

问题:反射创建对象代码很多。

其实除了dll的名称和类的全名称都是一样的代码,可以封装反射创建对象帮助类。
传入的参数都是字符串,可做成配置项,提高代码扩展性和灵活性,可以做到不修改代码而改变程序的功能。

1、反射创建对象封装

(1)创建SqlServerHelper的时候,没有依赖SqlServerHelper

(2)依赖的是两个字符串Business.DB.SqlServer.dll + Business.DB.SqlServer.SqlServerHelper,从配置文件读取。

(3)去掉个对细节的依赖的:依赖于抽象,不再依赖于细节;依赖倒置原则; 增强代码的稳定性;

using Business.DB.Interface;
using Microsoft.Extensions.Configuration;
using System;
using System.Reflection;

namespace MyReflecttion
{
    public class SimpleFactory
    {
        //创建SqlServerHelper的时候,没有依赖SqlServerHelper
        //依赖的是两个字符串Business.DB.SqlServer.dll + Business.DB.SqlServer.SqlServerHelper
        //去掉个对细节的依赖的:依赖于抽象,不再依赖于细节;依赖倒置原则; 增强代码的稳定性;
        public static IDBHelper CreateInstance()
        {  
            string ReflictionConfig = CustomConfigManager.GetConfig("ReflictionConfig"); 
            //Business.DB.SqlServer.SqlServerHelper,Business.DB.SqlServer.dll 
            string typeName = ReflictionConfig.Split(',')[0];
            string dllName = ReflictionConfig.Split(',')[1];

            //Assembly assembly = Assembly.LoadFrom("Business.DB.SqlServer.dll"); 
            //Type type = assembly.GetType("Business.DB.SqlServer.SqlServerHelper");

            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
    {
        //Core 读取配置文件:appsettings
        //1.Microsoft.Extensions.Configuration;
        //2.Microsoft.Extensions.Configuration.Json 
        public static string GetConfig(string key)
        {
            var builder = new ConfigurationBuilder().AddJsonFile("appsettings.json");  //默认读取  当前运行目录
            IConfigurationRoot configuration = builder.Build();
            string configValue = configuration.GetSection(key).Value;
            return configValue;
        }
    }
}

2. 配置文件

"ReflictionConfig": "Business.DB.Orcale.OrcaleHelper,Business.DB.Orcale.dll"

3、程序调用

如果公司来了一个新的技术经理:要将SqlServer换成MySql
(1)传统方式,必须要修改代码,然后必须要重新编译发布,步骤很多

(2)反射实现:断开了对普通类的依赖;依赖于配置文件+接口(抽象),做到了程序的可配置

(3)反射实现的步骤:按照接口约定实现一个Mysql帮助类库,Copy dll 文件到执行目录下,修改配置文件

IDBHelper helper1 = SimpleFactory.CreateInstance();
helper1.Query();

你可能感兴趣的:(c#,开发语言)