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
相关产品推荐
相关产品推荐

