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

如何正确获取IPrinterRepository接口的对应实现?

优化思路:告别Switch和硬编码全类名

我太懂这种纠结了——每次加新打印机实现都要改switch分支、维护常量字符串,烦得要死;直接存全类名又觉得像埋了个定时炸弹,哪天重构类名或命名空间,数据库里的记录全失效。给你两个不用新增额外依赖的优化方案,都是基于.NET原生能力的:

方案1:特性标记+自动映射(推荐频繁新增实现的场景)

核心思路是用自定义特性给每个实现类打标签,然后在启动时自动扫描并构建映射表,彻底告别手动维护switch和常量。

步骤1:定义标记特性

先写一个特性类,用来绑定打印机标识和实现类:

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

    public PrinterRepositoryAttribute(string printerCode)
    {
        PrinterCode = printerCode;
    }
}

步骤2:给实现类打标记

给每个IPrinterRepository的实现类加上这个特性,关联对应的打印机编码:

// 比如原来的Epson实现
[PrinterRepositoryAttribute(PrinterCode.Epson)]
public class EpsonRepository : IPrinterRepository 
{
    // 你的实现逻辑
}

[PrinterRepositoryAttribute(PrinterCode.HP)]
public class HPRepository : IPrinterRepository 
{
    // 你的实现逻辑
}

步骤3:自动注册映射与服务

在ConfigureServices里,扫描程序集自动构建映射表,同时注册所有实现类:

var assembly = Assembly.GetExecutingAssembly();

// 扫描所有IPrinterRepository的实现类,构建编码→类型的映射
var printerRepoMap = assembly.GetTypes()
    .Where(t => typeof(IPrinterRepository).IsAssignableFrom(t) 
                && !t.IsInterface 
                && !t.IsAbstract)
    .ToDictionary(
        t => t.GetCustomAttribute<PrinterRepositoryAttribute>()?.PrinterCode 
             ?? throw new InvalidOperationException($"类{t.Name}未标记PrinterRepositoryAttribute"),
        t => t
    );

// 注册映射表为单例
services.AddSingleton(printerRepoMap);

// 批量注册所有打印机仓库实现
foreach (var repoType in printerRepoMap.Values)
{
    services.AddTransient(repoType);
}

// 注册Resolver
services.AddSingleton<IPrinterRepositoryResolver, PrinterRepositoryResolver>();

步骤4:简化Resolver实现

现在Resolver只需要从映射表里取类型,再从DI容器获取实例,完全不用switch:

public class PrinterRepositoryResolver : IPrinterRepositoryResolver
{
    private readonly IServiceProvider _serviceProvider;
    private readonly Dictionary<string, Type> _printerRepoMap;

    public PrinterRepositoryResolver(IServiceProvider serviceProvider, Dictionary<string, Type> printerRepoMap)
    {
        _serviceProvider = serviceProvider;
        _printerRepoMap = printerRepoMap;
    }

    public IPrinterRepository GetRepository(string key)
    {
        if (!_printerRepoMap.TryGetValue(key, out var repoType))
        {
            throw new KeyNotFoundException("Service not implemented or not supported anymore!");
        }

        return (IPrinterRepository)_serviceProvider.GetService(repoType);
    }
}

这个方案的优势:

  • 符合开闭原则:新增打印机实现只需要加类、打特性,完全不用修改现有代码
  • 类型安全:编译时就能检查特性是否正确,不会出现全类名写错的问题
  • 彻底解放手动维护的麻烦,适合需要频繁扩展的场景

方案2:DI工厂模式(适合实现类较少、变动不频繁的场景)

如果不想用反射和特性,也可以把映射逻辑移到DI注册环节,让Resolver通过工厂函数获取实例,代码更简洁:

步骤1:注册服务与工厂函数

在ConfigureServices里,注册所有实现类,同时注册一个Func<string, IPrinterRepository>工厂:

// 先注册所有实现类
services.AddTransient<EpsonRepository>();
services.AddTransient<HPRepository>();

// 注册工厂函数,把编码和实例创建逻辑绑定
services.AddSingleton<Func<string, IPrinterRepository>>(sp => key =>
{
    return key switch
    {
        PrinterCode.Epson => sp.GetService<EpsonRepository>(),
        PrinterCode.HP => sp.GetService<HPRepository>(),
        _ => throw new KeyNotFoundException("Service not implemented or not supported anymore!")
    };
});

// 注册Resolver
services.AddSingleton<IPrinterRepositoryResolver, PrinterRepositoryResolver>();

步骤2:简化Resolver

现在Resolver只需要注入这个工厂函数,直接调用即可:

public class PrinterRepositoryResolver : IPrinterRepositoryResolver
{
    private readonly Func<string, IPrinterRepository> _repoFactory;

    public PrinterRepositoryResolver(Func<string, IPrinterRepository> repoFactory)
    {
        _repoFactory = repoFactory;
    }

    public IPrinterRepository GetRepository(string key)
    {
        return _repoFactory(key);
    }
}

这个方案的优势:

  • 无需反射,代码更直观
  • 把映射逻辑集中在DI配置环节,Resolver职责更单一
  • 适合实现类不多、很少变动的场景

为什么不推荐V2的全类名方案?

你觉得这个方案不够优雅是对的,它有几个硬伤:

  1. 耦合性极强:数据库直接绑定代码的内部结构,类名/命名空间一变,所有数据库记录都要更新,风险极大
  2. 无编译时检查:全类名写错只有运行时才会报错,调试麻烦
  3. 暴露内部细节:把代码实现细节暴露到持久化层,违反封装原则

总结

  • 如果你的打印机实现需要频繁新增,选方案1,彻底告别手动维护
  • 如果实现类很少变动,选方案2,代码简洁直观
  • 尽量避免V2的全类名方案,后续维护成本太高

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 06:43:37