依赖注入场景下使用空标记接口是否为合理技术方案?
结论先行
你当前用标记接口实现DI分类解析的方案完全合理,不属于需要规避的「代码坏味道」。
所谓空接口是坏味道的判断,通常针对的是完全不承担契约语义、仅为了满足某一处非业务逻辑的临时判断而强行加的标记。但你这里的IDynamicWorkspace、IStaticWorkspace本身就是对IWorkspace契约的语义补充,明确约定了实现类的分类属性,属于合法的接口语义边界划分,而且代码可读性极强,后续维护的人一眼就能看懂分类逻辑,没有额外的心智负担。
可选替代方案
你可以根据自己的业务场景选择是否替换:
- 方案1:使用.NET 8+ 原生支持的键控服务(
KeyedService)
如果你不想维护额外的空接口,且使用的.NET版本>=8,可以用这个官方原生的分类注入方案,注册和调用代码如下:
优势:不需要定义额外空接口,避免分类过多时的接口爆炸问题// 服务注册 builder.Services.AddKeyedSingleton<IWorkspace, Workspace1>("Dynamic"); builder.Services.AddKeyedSingleton<IWorkspace, Workspace2>("Static"); // Provider 实现 class WorkspaceProvider { private readonly IKeyedServiceProvider _keyedServiceProvider; public WorkspaceProvider(IKeyedServiceProvider keyedServiceProvider) { _keyedServiceProvider = keyedServiceProvider; } public IEnumerable<IWorkspace> GetWorkspaces() { var serviceKey = someCondition ? "Dynamic" : "Static"; return _keyedServiceProvider.GetKeyedServices<IWorkspace>(serviceKey); } }
劣势:服务键默认没有编译期校验,写错键名要运行时才会发现问题,可以换成枚举作为键规避这个问题。 - 方案2:特性标记+运行时过滤
如果你用的.NET版本低于8,不想引入空接口,且Workspace的实例数量不多,可以用特性打标记,拿到所有实例后过滤:
优势:不需要额外定义接口,不需要高版本.NET支持// 定义分类特性 [AttributeUsage(AttributeTargets.Class)] public class WorkspaceCategoryAttribute : Attribute { public WorkspaceCategory Category { get; } public WorkspaceCategoryAttribute(WorkspaceCategory category) => Category = category; } public enum WorkspaceCategory { Dynamic, Static } // 打标记到实现类 [WorkspaceCategory(WorkspaceCategory.Dynamic)] class Workspace1 : IWorkspace { } [WorkspaceCategory(WorkspaceCategory.Static)] class Workspace2 : IWorkspace { } // Provider 实现 class WorkspaceProvider { private readonly IEnumerable<IWorkspace> _allWorkspaces; public WorkspaceProvider(IEnumerable<IWorkspace> allWorkspaces) { _allWorkspaces = allWorkspaces; } public IEnumerable<IWorkspace> GetWorkspaces() { var targetCategory = someCondition ? WorkspaceCategory.Dynamic : WorkspaceCategory.Static; return _allWorkspaces.Where(x => x.GetType().GetCustomAttribute<WorkspaceCategoryAttribute>()?.Category == targetCategory); } }
劣势:需要反射过滤,且每次都会初始化所有Workspace实例,性能比标记接口、键控服务的方案差,不适合实例多、初始化成本高的场景。
最终建议
如果当前Workspace的分类不会频繁新增(<=3类),直接保留你现在的标记接口方案即可,是综合可读性、稳定性、性能最好的选择。只有当分类数量持续上涨,出现接口泛滥的问题时,再替换为键控服务方案即可。
内容的提问来源于stack exchange,提问作者DerTexas
相关产品推荐
相关产品推荐

