如何创建唯一且只读的枚举子集集合?
问题描述
我定义了一个大型枚举:
public enum RetirementTypes { k401 = 1, b403 = 2, b457 = 3, SimpleIRA = 4, Simple401k = 5, TIRA = 6, RIRA = 7, RothIRA = 8, Solo401k = 9, SEPIRA = 10, FA = 11, VA = 12, ESOP = 14, PERA = 15, VL = 16, SpousalIRA = 17, BIRA = 19, OtherRetirement = 22, TSP = 23, OtherIra = 24, BRoth = 33 // 其他枚举项 }
我希望在部分组件中定义该枚举的子集,要求:
- 子集是常量/只读的
- 包含唯一值
- 最好能在编译时发现重复值问题
目前我用IReadOnlyCollection来定义子集,但存在重复值无法提前发现的问题,比如:
IReadOnlyCollection<RetirementTypes> RETIREMENT_IRA_TYPES = [ RetirementTypes.SEPIRA, RetirementTypes.BIRA, RetirementTypes.RothIRA, RetirementTypes.OtherIra, RetirementTypes.RIRA, RetirementTypes.SpousalIRA, RetirementTypes.OtherIra, // 重复项 RetirementTypes.RothIRA // 重复项 ];
这个子集用于查询过滤,比如:
iw.WbRetirements.Where(x => x.Owner == (int)OwnerType.Self && RETIREMENT_IRA_TYPES.Contains((RetirementTypes)x.RetirementPlanType)).Sum(b => b.Balance);
请问有没有办法实现满足要求的唯一、只读集合?
解决方案
1. 只读HashSet + 编译时分析器(最优解)
实现方式:
用HashSet<RetirementTypes>存储子集,再通过AsReadOnly()转为只读集合,保证运行时唯一性;同时编写自定义Roslyn分析器,在编译时扫描集合初始化代码,检测重复枚举值。
代码示例:
// 定义只读的唯一集合 public static readonly IReadOnlySet<RetirementTypes> RETIREMENT_IRA_TYPES = new HashSet<RetirementTypes> { RetirementTypes.SEPIRA, RetirementTypes.BIRA, RetirementTypes.RothIRA, RetirementTypes.OtherIra, RetirementTypes.RIRA, RetirementTypes.SpousalIRA }.AsReadOnly();
编译时检查:
如果团队有能力,可以编写简单的Roslyn分析器,标记需要检查的集合(比如用自定义属性),扫描初始化列表中的重复值并触发编译错误。如果没有自定义分析器的条件,也可以在CI流程中添加脚本,扫描这类集合定义,发现重复就终止构建。
2. 静态类常量集合(编译时自动检测重复)
如果子集数量固定且不多,可以用静态类的常量字段定义,IDE会自动提示重复的常量赋值:
public static class IraRetirementTypes { public const RetirementTypes SEPIRA = RetirementTypes.SEPIRA; public const RetirementTypes BIRA = RetirementTypes.BIRA; public const RetirementTypes RothIRA = RetirementTypes.RothIRA; public const RetirementTypes OtherIra = RetirementTypes.OtherIra; public const RetirementTypes RIRA = RetirementTypes.RIRA; public const RetirementTypes SpousalIRA = RetirementTypes.SpousalIRA; // 提供只读集合供查询使用 public static readonly IReadOnlySet<RetirementTypes> All = new HashSet<RetirementTypes> { SEPIRA, BIRA, RothIRA, OtherIra, RIRA, SpousalIRA }.AsReadOnly(); }
使用时直接调用IraRetirementTypes.All.Contains(...)即可,重复定义常量会立即被IDE识别并报错。
3. 自定义只读集合类(运行时快速报错)
创建一个自定义的只读集合类,在构造函数中检查重复值,初始化时发现重复就抛出异常:
public class UniqueReadOnlyEnumSet<T> : IReadOnlySet<T> where T : Enum { private readonly HashSet<T> _innerSet; public UniqueReadOnlyEnumSet(params T[] items) { _innerSet = new HashSet<T>(items); if (_innerSet.Count != items.Length) { throw new ArgumentException("集合中存在重复的枚举值"); } } // 实现IReadOnlySet<T>接口方法,委托给_innerSet public bool Contains(T item) => _innerSet.Contains(item); public bool IsProperSubsetOf(IEnumerable<T> other) => _innerSet.IsProperSubsetOf(other); public bool IsProperSupersetOf(IEnumerable<T> other) => _innerSet.IsProperSupersetOf(other); public bool IsSubsetOf(IEnumerable<T> other) => _innerSet.IsSubsetOf(other); public bool IsSupersetOf(IEnumerable<T> other) => _innerSet.IsSupersetOf(other); public bool Overlaps(IEnumerable<T> other) => _innerSet.Overlaps(other); public bool SetEquals(IEnumerable<T> other) => _innerSet.SetEquals(other); public IEnumerator<T> GetEnumerator() => _innerSet.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); }
使用方式:
public static readonly UniqueReadOnlyEnumSet<RetirementTypes> RETIREMENT_IRA_TYPES = new( RetirementTypes.SEPIRA, RetirementTypes.BIRA, RetirementTypes.RothIRA, RetirementTypes.OtherIra, RetirementTypes.RIRA, RetirementTypes.SpousalIRA );
这种方式虽然是运行时检查,但能在程序启动或单元测试阶段快速发现重复问题。
4. 源生成器(进阶编译时方案)
编写C#源生成器,通过自定义属性标记枚举子集,比如[EnumSubset(typeof(RetirementTypes), "SEPIRA,BIRA,RothIRA")],生成器解析属性参数后自动生成包含唯一值的只读集合代码,同时在生成过程中检查重复值,若存在重复则触发编译错误。
内容的提问来源于stack exchange,提问作者D A
相关产品推荐
相关产品推荐

