能否为ICommand与ICommand<TResult>创建统一的ICommandHandler接口?
统一ICommandHandler接口适配两种命令类型的实现方案
当然可以做到!这种统一接口的设计不仅可行,还能让你的命令处理体系更简洁一致,在命令模式或CQRS的实践中非常实用。下面我来给你拆解具体的实现思路和代码示例:
第一步:定义基础命令接口
首先我们需要把两种命令类型做层级划分,让带返回结果的命令继承无返回的基础命令,保证类型体系的连贯性:
// 无返回结果的基础命令标记接口 public interface ICommand { } // 带返回结果的命令接口,继承自ICommand public interface ICommand<TResult> : ICommand { }
第二步:定义统一的命令处理程序接口
利用泛型的灵活性,我们可以创建一个支持两种命令类型的通用处理程序接口,这里使用逆变(in TCommand)和协变(out TResult)来提升接口的灵活性:
public interface ICommandHandler<in TCommand, out TResult> where TCommand : ICommand { TResult Handle(TCommand command); }
第三步:处理无返回结果的命令
因为C#不允许直接把void作为泛型参数,我们可以定义一个空的Unit结构体来标记“无返回结果”——这是很多成熟框架(比如MediatR)常用的设计思路:
// 用于表示无返回结果的标记类型 public struct Unit { }
接下来,无返回命令的处理程序就可以返回这个Unit类型:
// 示例无返回命令:创建用户 public class CreateUserCommand : ICommand { public string Username { get; set; } public string Email { get; set; } } // 对应的处理程序 public class CreateUserCommandHandler : ICommandHandler<CreateUserCommand, Unit> { public Unit Handle(CreateUserCommand command) { // 执行创建用户的业务逻辑:比如写入数据库 Console.WriteLine($"Created user {command.Username}"); return default; // 返回默认的Unit实例即可 } }
第四步:处理带返回结果的命令
带返回结果的命令处理就更直接了,直接指定泛型的TResult为你需要的返回类型即可:
// 示例带返回命令:根据ID获取用户 public class GetUserByIdCommand : ICommand<User> { public Guid UserId { get; set; } } // 用户实体类 public class User { public Guid Id { get; set; } public string Username { get; set; } } // 对应的处理程序 public class GetUserByIdCommandHandler : ICommandHandler<GetUserByIdCommand, User> { public User Handle(GetUserByIdCommand command) { // 模拟从数据库查询用户 return new User { Id = command.UserId, Username = "JohnDoe" }; } }
额外优化:统一命令调度器
有了统一的Handler接口后,你可以轻松实现一个通用的命令调度器,不需要分别处理两种命令类型:
public class CommandDispatcher { private readonly IServiceProvider _serviceProvider; public CommandDispatcher(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } // 调度带返回结果的命令 public TResult Dispatch<TCommand, TResult>(TCommand command) where TCommand : ICommand<TResult> { var handler = _serviceProvider.GetRequiredService<ICommandHandler<TCommand, TResult>>(); return handler.Handle(command); } // 调度无返回结果的命令 public Unit Dispatch<TCommand>(TCommand command) where TCommand : ICommand { var handler = _serviceProvider.GetRequiredService<ICommandHandler<TCommand, Unit>>(); return handler.Handle(command); } }
这种设计的核心优势在于:
- 避免维护两个独立的Handler接口,减少代码冗余
- 命令调度器的实现更简洁,不需要分支判断命令类型
- 泛型约束保证了类型安全,编译时就能发现错误
内容的提问来源于stack exchange,提问作者Mike
相关产品推荐
相关产品推荐

