You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 09:35:18