NET反射机制:动态方法调用的实现与优化

背景与需求分析

最近在开发一个工控机环境部署工具时,遇到了一个差异化的需求:不同版本的Web项目需要执行不同的系统环境检测流程。如果每次部署都要根据配置文件动态调用不同的检测方法,有的需要实例化对象,有的要调用静态方法,甚至还要处理异步任务,就会将维护工作搞的很繁重。

传统的硬编码方式显然无法满足这种灵活需求,于是我决定采用.NET的反射机制。作为NET核心功能之一,反射就像程序的"自省"能力,允许我们在运行时探索类型信息、动态创建对象并执行方法。

设计思路

整个方案围绕三个核心环节展开:

  1. 配置驱动:用JSON定义要执行的方法
  2. 动态解析:通过反射加载程序集、查找类型和方法
  3. 智能执行:自动处理同步/异步方法调用

开发工具

  • 开发环境: Visual Studio 2022
  • 目标框架: .NET 8
  • 依赖库 : System.ReflectionSystem.Text.Json(用于JSON配置解析)

实现思路

1.动态方法配置定义:声明方法反射的配置,比如方法名称、是否需要创建实例、是否异步任务 2.动态方法解析实现:根据传入配置解析程序集、解析类型、获取方法、创建实例等 3.动态方法执行实现:根据获取到的方法、实例、方法执行方式,通过Method.invoke完成方法调用 4.执行方法测试:配置一个文件,测试方法执行

代码实现解析

1.配置模型设计

csharp 复制代码
    /// <summary>
    /// 方法反射配置
    /// </summary>
    public class MethodReflectConfig
    {
        /// <summary>
        /// 执行函数名称
        /// 程序集名称::命名空间.类名称::方法名称
        /// </summary>
        public string Function { get; set; }

        /// <summary>
        /// 是否需要创建新实例
        /// </summary>
        public bool Instance { get; set; }

    }

2.反射解析器实现

csharp 复制代码
    /// <summary>
    ///解析方法和方法类型,同时返回创建实例
    /// </summary>
    /// <param name="functionPath"> 程序集名称::命名空间.类名称::方法名称,
    /// 例如 YourDLLName::YourNamespace.SystemDetector::GetSystemInfoAsync
    /// </param>
    /// <param name="bInstance">是否需要创建新实例</param>
    /// <returns></returns>
    /// <exception cref="FormatException"></exception>
    /// <exception cref="TypeLoadException"></exception>
    /// <exception cref="MissingMethodException"></exception>
    public static (object instance, Type classtype, MethodInfo method) ParseMethod(MethodReflectConfig config)
    {
        //解析函数路径
        var parts = config.Function.Split("::");
        if (parts.Length != 3) throw new FormatException("函数路径格式错误");
        //加载程序集
        var assemblyName = parts[0].Trim();
        var assembly = Assembly.Load(assemblyName)
            ?? throw new TypeLoadException($"未能加载程序集: {parts[0]}");
        //获取类型
        var className = parts[1].Trim();
        var classtype = assembly.GetType(className)
            ?? throw new TypeLoadException($"未能找到类型: {parts[1]}");   
        // 创建实例(如果需要)
        object instance = config.Instance ? 
            Activator.CreateInstance(type) : 
            null;   
        // 获取方法
        const BindingFlags flags = BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance;
        var method = type.GetMethod(parts[2].Trim(), flags)
           ?? throw new MissingMethodException($"方法 {parts[2]} 未找到");  

        //返回结果
        return (instance, classtype, method);
    }

3.动态方法执行实现

csharp 复制代码
    /// <summary>
    /// 执行动态方法
    /// </summary>
    /// <param name="config"></param>
    /// <param name="parameters"></param>
    /// <returns></returns>
    public async Task<object> ExecuteMethodAsync(MethodReflectConfig config, object[] parameters)
    {
        var (instance, _, method) = AssemblyHelper.ParseMethod(config);

        // 获取默认参数(适用于可选参数)
        var paramInfos = method.GetParameters();
        parameters = parameters ?? new object[paramInfos.Length];
        for (int i = 0; i < paramInfos.Length; i++)
        {
            if (i >= parameters.Length || parameters[i] == null)
            {
                if (paramInfos[i].HasDefaultValue)
                {
                    parameters[i] = paramInfos[i].DefaultValue;
                }
            }
        }

        var methodResult = method.Invoke(instance, parameters);
        //处理异步方法
        if (methodResult is Task task)
        {
            await task.ConfigureAwait(false);

            // 处理有返回值的Task<T>
            if (task.GetType().IsGenericType)
            {
                return task.GetType().GetProperty("Result")?.GetValue(task);
            }
            return null; // 无返回值的Task
        }

        // 处理同步方法返回值
        return methodResult;
    }

实战测试

1.上个配置文件 (1)创建配置文件,存储名称为 xx.json (2)由于有中文名称,注意文件存储为utf-8格式 (3)如果不想自己手写或者写代码生成json文件,将类定义传递给deepseek或者豆包等助手,让他帮你自动生成

json 复制代码
{
  "Steps": [
    {
      //名称
      "Name": "检测系统信息",
      //描述
      "Description": "检测系统信息",
	  "FunctionConfig":{
         //方法路径
		 "Function": "DLLName::YourNameSpace.ClassName::FunctiongName1"
	  },   
      //执行失败是否终止流程 
      "HaltOnError": false
    },
    {
      "Name": "检测系统注册",
      "Description": "检测系统注册",
	  "FunctionConfig":{
		 "Function": "DLLName::YourNameSpace.ClassName::FunctiongName1"	  
	  },
      "HaltOnError": false
    },
    {
      "Name": "检测磁盘信息",
      "Description": "检测磁盘信息",
	  "FunctionConfig":{
		 "Function": "DLLName::YourNameSpace.ClassName::FunctiongName1"
	  },
	  "FunctionConfig2":{
		   "Function": "DLLName::YourNameSpace.ClassName::FunctiongName1",
		   "Instance":true
	  },
      "HaltOnError": false
    }
  ]
}

2.自己写一个载入代码 (1)jsonhelper读取json文件为执行对象列表steps (2)按顺序执行对象函数列表 (3)此处CallBack为回调,根据自己需求定义

csharp 复制代码
    /// <summary>
    /// 执行系统体检
    /// </summary>
    /// <returns></returns>
    public async Task ExecuteDetectFlowAsync(Action<string, object, float> CallBack)
    {
        var steps = LoadConfigFromJson("xx.json"); 
        int totalSteps = steps.Count;

        for (int index = 0; index < totalSteps; index++)
        {
            var step = steps[index];
            try
            {
                // 执行第一步检测
                var result1 = await ExecuteMethodAsync(step.FunctionConfig, null);

                using (var disposableResult = result1 as IDisposable)
                {
                    _WriteModuleLog($"[{step.Name}] Result1:{JsonHelper.ToJson(disposableResult)}");

                    // 执行可选的第二步处理
                    object finalResult = disposableResult;
                    if (step.FunctionConfig2 != null)
                    {
                        var parameters = new object[] { disposableResult, _configManager.GetSysDiagnosisConfig() };
                        finalResult = await ExecuteMethodAsync(step.FunctionConfig2, parameters);
                        _WriteModuleLog($"[{step.Name}] Result2:{JsonHelper.ToJson(finalResult)}");
                    }
                    CallBack(step.Name, finalResult, (float)(index + 1) / totalSteps);
                }
            }
            catch (Exception ex)
            {
                SeriLogHelper.LogException(ex, $"[{step.Name}] 执行失败: {ex.Message}");
                if (step.HaltOnError) break;
            }
        }
    }

总结与扩展思考

通过反射机制实现动态方法调用,显著提升了系统的灵活性和可配置性,适应了我当前项目的需求。

  • 适用场景:适合插件化架构、流程动态编排等场景,但频繁调用时需谨慎性能损耗。
  • 扩展方向:支持依赖注入、方法参数自动绑定(如从环境变量或数据库读取参数)。

总结与扩展思考

通过反射机制实现动态方法调用,显著提升了系统的灵活性和可配置性,适应了我当前项目的需求。

  • 适用场景:适合插件化架构、流程动态编排等场景,但频繁调用时需谨慎性能损耗。
  • 扩展方向:支持依赖注入、方法参数自动绑定(如从环境变量或数据库读取参数)。

最终目标:在灵活性与性能之间找到平衡,为动态化需求提供可靠的技术支撑。

相关推荐
NotOnlyCoding2 小时前
微软 NativeAOT
.net·ai编程
源之缘-OFD先行者12 小时前
WPF 与 GMap.NET 结合实现雷达目标动态显示与地图绘制
信息可视化·.net·wpf
PfCoder18 小时前
C#枚举(Enum)详解
开发语言·c#·.net·winform
追逐时光者1 天前
全面的 .NET 操作 SQLite 入门实战(包含选型、开发、发布、部署)!
后端·.net
码观天工1 天前
10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
c#·.net·软件工程·思维·封装·面相对象
小码编匠2 天前
C# 编程技巧实现屏幕录制功能
后端·c#·.net
小码编匠2 天前
C# + WinForm 基于ModBus协议的智能仓储温控管理系统
后端·c#·.net
胖头鱼不吃鱼-2 天前
AI与.NET技术实操系列:ML.NET篇
人工智能·.net
追逐时光者2 天前
基于 .NET Blazor 开源、低代码、易扩展的插件开发框架
后端·.net