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

能否为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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 08:10:38