如何在C#中实现支持任意参数与返回值的方法调用指令系统
嵌入式系统自动化项目的C#灵活指令引擎实现方案
我正在为嵌入式系统自动化项目开发一款C#脚本引擎,核心需求是按顺序存储任意方法及其参数,后续调用instructions.Execute()即可顺序执行。这些指令对应的方法无固定模式:
- 支持任意返回值类型(void、Task、object等)
- 支持任意参数(无参、单参、多参,最好能引用外部变量)
- 多数是硬件相关的异步方法,需要通过await执行
现有方案局限
之前用泛型实现了单参数版本的概念验证,但无法支持多参方法,代码如下:
public class Instruction<T, U> { /// The method to be executed. The method must have one argument. private readonly Func<T, U>? _Function; /// The method to be executed. The method must have one argument. private readonly Action<T>? _Action; /// The argument going to be passed to the method private readonly T _Argument; // TODO: This could be a list of arguments for more than 1 argument methods public Instruction(Func<T, U> function, T argument) { _Function = function; _Argument = argument; _Action = null; } public Instruction(Action<T> function, T argument) { _Function = null; _Argument = argument; _Action = function; } /// Executes the function(argument) passed in the constructor. public async Task<bool> Execute() { if (_Action != null) { _Action(_Argument); return true; } else if (_Function == null) // _Action and _Function is null. { return true; } // Invoke the function with the argument var result = _Function(_Argument); // Check if the result is a Task if (result is Task task) { // If it's a Task, await it await task; } else if (result is Task<U> taskWithResult) { // If it's a Task<U>, await it and ignore the result await taskWithResult; } else { // If it's neither, it's a synchronous operation, so no awaiting is necessary // This branch is executed for synchronous methods like Console.WriteLine } return true; } }
期望的使用方式示例:
Test test = new Test(); test.AddInstruction(Console.WriteLine, "Silly Example"); for (var index = 0; index < 10; index++) { test.AddInstruction(HMI.ButtonPress, index); test.AddInstruction(Task.Delay, 250); } test.AddInstruction(ECU.TurnOnBlinker, LIGHTS.LeftBlinker); test.AddInstruction(Task.Delay, 1000); test.AddInstruction(ECU.TurnOffBlinker, LIGHTS.LeftBlinker); test.AddInstruction(Console.WriteLine, "Executed all instructions"); test.Execute();
可行实现方案
1. 核心Instruction类(统一封装为Func)
通过静态工厂方法处理所有类型的方法,将其统一封装为Func<Task>,消除参数和返回值的差异:
public class Instruction { private readonly Func<Task> _executeFunc; private Instruction(Func<Task> executeFunc) { _executeFunc = executeFunc ?? throw new ArgumentNullException(nameof(executeFunc)); } // 无参同步方法 public static Instruction Create(Action action) { return new Instruction(() => { action(); return Task.CompletedTask; }); } // 无参异步方法 public static Instruction Create(Func<Task> func) { return new Instruction(func); } // 单参同步方法 public static Instruction Create<T>(Action<T> action, T arg) { return new Instruction(() => { action(arg); return Task.CompletedTask; }); } // 双参同步方法 public static Instruction Create<T1, T2>(Action<T1, T2> action, T1 arg1, T2 arg2) { return new Instruction(() => { action(arg1, arg2); return Task.CompletedTask; }); } // 单参异步方法 public static Instruction Create<T>(Func<T, Task> func, T arg) { return new Instruction(() => func(arg)); } // 带返回值的异步方法(忽略返回值,如需保存可扩展) public static Instruction Create<TResult>(Func<Task<TResult>> func) { return new Instruction(async () => await func()); } // 执行指令 public async Task Execute() { await _executeFunc(); } }
2. 指令集合管理类
提供简洁的添加和批量执行接口:
public class InstructionSequence { private readonly List<Instruction> _instructions = new List<Instruction>(); // 基础添加方法 public void AddInstruction(Instruction instruction) { _instructions.Add(instruction); } // 重载添加方法,简化调用 public void AddInstruction(Action action) => AddInstruction(Instruction.Create(action)); public void AddInstruction(Func<Task> func) => AddInstruction(Instruction.Create(func)); public void AddInstruction<T>(Action<T> action, T arg) => AddInstruction(Instruction.Create(action, arg)); public void AddInstruction<T1, T2>(Action<T1, T2> action, T1 arg1, T2 arg2) => AddInstruction(Instruction.Create(action, arg1, arg2)); public void AddInstruction<T>(Func<T, Task> func, T arg) => AddInstruction(Instruction.Create(func, arg)); // 批量执行所有指令 public async Task ExecuteAll() { foreach (var instruction in _instructions) { await instruction.Execute(); } } }
3. 实际使用示例
var sequence = new InstructionSequence(); sequence.AddInstruction(Console.WriteLine, "Silly Example"); for (int i = 0; i < 10; i++) { // 捕获循环变量,避免闭包陷阱 var index = i; sequence.AddInstruction(HMI.ButtonPress, index); sequence.AddInstruction(() => Task.Delay(250)); } sequence.AddInstruction(ECU.TurnOnBlinker, LIGHTS.LeftBlinker); sequence.AddInstruction(() => Task.Delay(1000)); sequence.AddInstruction(ECU.TurnOffBlinker, LIGHTS.LeftBlinker); sequence.AddInstruction(Console.WriteLine, "Executed all instructions"); await sequence.ExecuteAll();
方案优势
- 完全灵活:支持任意参数数量、任意同步/异步方法
- 逻辑统一:所有指令都封装为
Func<Task>,执行时只需统一await - 支持外部变量引用:通过lambda捕获外部变量(注意循环变量的闭包陷阱,如示例中用
var index = i) - 扩展性强:如需保存方法返回值,只需扩展Instruction类添加返回值存储逻辑
内容的提问来源于stack exchange,提问作者Breno
相关产品推荐
相关产品推荐

