.NET 6控制台程序:如何移除Switch实现参数与Action动态映射
移除.NET 6控制台应用中的Switch语句,实现动态Action关联
问题背景
现有一个.NET 6控制台应用,通过命令行参数触发对应操作。当前使用switch语句匹配参数与Action类,新增操作时必须修改switch分支,缺乏动态性。目标是让开发人员只需编写实现IAction接口的Action类,无需修改主逻辑,程序自动完成参数与Action的关联。
原主程序代码:
namespace ConsoleApp1 { internal class Program { static void Main(string[] args) { // validating args if (!args.Any()) return; switch (args.First()) { case "action1": case "action2": case "action3": break; default: return; } switch (args.First()) { case "action1": RunTheAction<Action1>(); break; case "action2": RunTheAction<Action2>(); break; case "action3": //todo RunTheAction<Action3>() break; // it keeps growing -> RunTheAction<>() } } static void RunTheAction<T>() where T : IAction { var action = Activator.CreateInstance<T>(); action.Execute(); } } }
IAction接口及实现示例:
internal interface IAction { void Execute(); } internal class Action1 : IAction { public void Execute() { // do some action1 stuff } } internal class Action2 : IAction { public void Execute() { // do some action2 stuff (different than action1 :) ) } }
解决方案
方案1:自定义特性+反射扫描
通过特性标记Action类对应的命令参数,程序启动时自动扫描所有IAction实现类,建立参数与类型的映射。
步骤1:定义命令特性
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] internal class ActionCommandAttribute : Attribute { public string CommandName { get; } public ActionCommandAttribute(string commandName) { CommandName = commandName; } }
步骤2:给Action类添加特性
[ActionCommand("action1")] internal class Action1 : IAction { public void Execute() { // do some action1 stuff } } [ActionCommand("action2")] internal class Action2 : IAction { public void Execute() { // do some action2 stuff } }
步骤3:重构主程序逻辑
internal class Program { static void Main(string[] args) { if (!args.Any()) return; var commandActionMap = GetCommandActionMap(); if (!commandActionMap.TryGetValue(args.First(), out var actionType)) return; RunTheAction(actionType); } // 扫描程序集,构建命令与Action类型的映射 static Dictionary<string, Type> GetCommandActionMap() { var map = new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase); var actionTypes = typeof(IAction).Assembly.GetTypes() .Where(t => typeof(IAction).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract); foreach (var type in actionTypes) { var attr = type.GetCustomAttribute<ActionCommandAttribute>(); if (attr != null && !string.IsNullOrEmpty(attr.CommandName)) { map[attr.CommandName] = type; } } return map; } // 执行指定类型的Action static void RunTheAction(Type actionType) { if (Activator.CreateInstance(actionType) is IAction action) { action.Execute(); } } }
方案2:约定优先(无需额外特性)
如果命令参数名称与Action类名(小写)一致,可直接通过类名约定建立映射,无需添加特性,实现更简洁。
重构主程序逻辑
internal class Program { static void Main(string[] args) { if (!args.Any()) return; var commandActionMap = GetCommandActionMap(); if (!commandActionMap.TryGetValue(args.First(), out var actionType)) return; RunTheAction(actionType); } // 基于类名约定构建映射:类名转小写作为命令参数 static Dictionary<string, Type> GetCommandActionMap() { var map = new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase); var actionTypes = typeof(IAction).Assembly.GetTypes() .Where(t => typeof(IAction).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract); foreach (var type in actionTypes) { var commandName = type.Name.ToLowerInvariant(); map[commandName] = type; } return map; } static void RunTheAction(Type actionType) { if (Activator.CreateInstance(actionType) is IAction action) { action.Execute(); } } }
此方案下,Action1对应命令参数action1,Action2对应action2,新增Action类后自动生效。
方案3:依赖注入(适合复杂场景)
若后续Action需要依赖其他服务,可使用.NET依赖注入容器管理Action实例,便于扩展和测试。
步骤1:引入依赖注入包
在项目中安装Microsoft.Extensions.DependencyInjection NuGet包。
步骤2:重构主程序逻辑
using Microsoft.Extensions.DependencyInjection; internal class Program { static void Main(string[] args) { if (!args.Any()) return; // 构建服务容器并注册所有IAction实现 var services = new ServiceCollection(); RegisterActions(services); using var serviceProvider = services.BuildServiceProvider(); // 根据命令参数查找对应的Action实例 var command = args.First(); var action = serviceProvider.GetServices<IAction>() .FirstOrDefault(a => a.GetType().Name.ToLowerInvariant() == command); action?.Execute(); } // 注册所有IAction实现类 static void RegisterActions(IServiceCollection services) { var actionTypes = typeof(IAction).Assembly.GetTypes() .Where(t => typeof(IAction).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract); foreach (var type in actionTypes) { services.AddTransient(typeof(IAction), type); } } }
若需要自定义命令名,可结合方案1的特性,修改FirstOrDefault中的判断逻辑即可。
方案对比
- 特性标记:灵活性最高,支持自定义命令名,适合命令名与类名不一致的场景;
- 约定优先:实现最简单,无额外代码,适合命令名与类名一一对应的场景;
- 依赖注入:适合需要注入服务的复杂场景,便于单元测试和扩展。
内容的提问来源于stack exchange,提问作者DevSurferPTPXO
相关产品推荐
相关产品推荐

