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

C#:无需反射从命名空间加载类实现终端命令注册

无需反射的命令注册方案

针对你的C#终端命令注册需求,以下是几种无反射、高性能的替代方案,兼顾代码整洁性和运行时性能:


方案1:静态构造函数+类型引用触发注册

利用C#静态构造函数的特性,让每个命令类在首次被访问时自动完成注册,同时通过集中的类型列表确保类被加载,避免手动逐个调用注册方法。

步骤1:定义全局命令注册表

public static class CommandRegistry
{
    public static Dictionary<string, ICommand> Commands { get; } = new(StringComparer.OrdinalIgnoreCase);

    public static void Register(string commandName, ICommand command)
    {
        if (!Commands.ContainsKey(commandName))
        {
            Commands.Add(commandName, command);
        }
    }
}

public interface ICommand
{
    void Execute(string[] args);
}

步骤2:命令类实现静态构造函数

每个命令类的静态构造函数自动向注册表注册自身:

namespace Server.Cli.Commands
{
    public class ChdirCommand : ICommand
    {
        static ChdirCommand()
        {
            CommandRegistry.Register("chdir", new ChdirCommand());
        }

        public void Execute(string[] args)
        {
            // chdir命令逻辑实现
        }
    }

    public class RdCommand : ICommand
    {
        static RdCommand()
        {
            CommandRegistry.Register("rd", new RdCommand());
        }

        public void Execute(string[] args)
        {
            // rd命令逻辑实现
        }
    }
}

步骤3:触发静态构造函数

创建一个加载类,通过引用所有命令类型确保静态构造函数执行:

public static class CommandLoader
{
    // 新增命令时,仅需在此添加对应typeof即可
    private static readonly Type[] _commandTypes = new[]
    {
        typeof(ChdirCommand),
        typeof(RdCommand)
    };

    public static void Initialize()
    {
        // 访问数组触发类型加载,无需实际使用
        _ = _commandTypes.Length;
    }
}

使用时只需调用CommandLoader.Initialize(),即可完成所有命令注册,无反射开销,代码维护成本低。


方案2:编译时代码生成(Source Generator)

利用C#源代码生成器,在编译时自动扫描命令类并生成注册代码,完全替代手动注册和反射,运行时零额外开销。

步骤1:定义命令标记属性

[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class TerminalCommandAttribute : Attribute
{
    public string CommandName { get; }

    public TerminalCommandAttribute(string commandName)
    {
        CommandName = commandName;
    }
}

步骤2:标记命令类

namespace Server.Cli.Commands
{
    [TerminalCommand("chdir")]
    public class ChdirCommand : ICommand
    {
        public void Execute(string[] args)
        {
            // 命令逻辑
        }
    }
}

步骤3:实现源代码生成器

创建单独的类库项目,实现ISourceGenerator:

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.Text;

[Generator]
public class CommandRegistrationGenerator : ISourceGenerator
{
    public void Initialize(GeneratorInitializationContext context) { }

    public void Execute(GeneratorExecutionContext context)
    {
        // 扫描所有标记了TerminalCommandAttribute的ICommand实现类
        var commandSymbols = context.Compilation.SyntaxTrees
            .SelectMany(tree => tree.GetRoot().DescendantNodes().OfType<ClassDeclarationSyntax>())
            .Select(cls => context.Compilation.GetSemanticModel(cls.SyntaxTree).GetDeclaredSymbol(cls))
            .OfType<INamedTypeSymbol>()
            .Where(symbol => symbol.GetAttributes().Any(a => 
                a.AttributeClass?.Name == nameof(TerminalCommandAttribute)) &&
                symbol.AllInterfaces.Any(i => i.Name == nameof(ICommand)));

        // 自动生成注册代码
        var sourceBuilder = new StringBuilder(@"
using Server.Cli;
using Server.Cli.Commands;

namespace Server.Cli.Generated
{
    public static class AutoCommandRegistrar
    {
        public static void RegisterAllCommands()
        {
");

        foreach (var symbol in commandSymbols)
        {
            var attribute = symbol.GetAttributes().First(a => 
                a.AttributeClass?.Name == nameof(TerminalCommandAttribute));
            var commandName = attribute.ConstructorArguments[0].Value.ToString();
            sourceBuilder.AppendLine($"            CommandRegistry.Register(\"{commandName}\", new {symbol.Name}());");
        }

        sourceBuilder.Append(@"
        }
    }
}");

        context.AddSource("AutoCommandRegistrar.g.cs", sourceBuilder.ToString());
    }
}

步骤4:调用自动生成的注册方法

在主项目中引用生成器类库,初始化时调用:

AutoCommandRegistrar.RegisterAllCommands();

此方案完全自动化,新增命令仅需添加[TerminalCommand]属性,编译时自动生成注册代码,运行时无任何反射或初始化开销。


方案3:模块化手动注册

如果不想使用静态构造或代码生成,可以将手动注册逻辑按功能分组,通过部分类拆分代码,避免混乱。

示例代码

// CommandRegistrar.cs - 主入口
public static partial class CommandRegistrar
{
    public static void RegisterAllCommands()
    {
        RegisterFileSystemCommands();
        RegisterSystemCommands();
        // 其他功能分组
    }
}

// CommandRegistrar.FileSystem.cs - 文件系统命令分组
public static partial class CommandRegistrar
{
    private static void RegisterFileSystemCommands()
    {
        ChdirCommand.Register();
        RdCommand.Register();
        // 新增文件系统命令时在此添加
    }
}

// CommandRegistrar.System.cs - 系统命令分组
public static partial class CommandRegistrar
{
    private static void RegisterSystemCommands()
    {
        // 系统相关命令注册
    }
}

此方案完全无反射,性能最优,代码结构清晰,适合对编译时工具不熟悉的场景。

内容的提问来源于stack exchange,提问作者Undead

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 00:25:31