C#中如何检查泛型枚举是否为有效非默认值?
问题:泛型枚举类型的初始值判断与转换逻辑优化
我在C#应用中封装了UnitsNet库的抽象类,用于管理带单位的数值输入、限制可用单位并处理复杂输入。基类和派生类代码如下:
public abstract class PropertyBase<TQuantity, TUnit> : where TQuantity : IQuantity where TUnit : Enum { public string InputString { get; set; } public TUnit? SelectedUnit { get => _selectedUnit; set { var oldUnit = _selectedUnit; _selectedUnit = value; if (oldUnit != null) //此检查无效 { //仅当oldUnit不为空或0时执行此代码 ConvertInputString(oldUnit, value); } } } private TUnit? _selectedUnit; protected abstract TUnit[] AvailableUnits { get; } protected abstract TQuantity FromValue(double value, TUnit unit); protected abstract double As(TQuantity quantity, TUnit unit); private void ConvertInputString(TUnit oldUnit, TUnit newUnit) { //解析并转换InputString中的值 } protected List<double> ParseInputValues(string input) { //... } } public class InputPropertyAngles : PropertyBase<Angle, AngleUnit>, IInputProperty { public InputPropertyAngle() { SelectedUnit = AngleUnit.Degree; } public override double[] ValuesInSi { get { var values = ParseInputValues(InputString).ToArray(); return values.Select(value => Angle.From(value, SelectedUnit).Degrees).ToArray(); } } protected override AngleUnit[] AvailableUnits => [AngleUnit.Degree, AngleUnit.Radian]; protected override Angle FromValue(double value, AngleUnit unit) => Angle.From(value, unit); protected override double As(Angle quantity, AngleUnit unit) => quantity.As(unit); }
注:AngleUnit是UnitsNet库中的枚举。
当前需求:实例化InputPropertyAngle时,在构造函数中设置默认单位AngleUnit.Degree时,不执行ConvertInputString方法。原判断逻辑if (oldUnit != null)无效(因为TUnit是值类型枚举,无法为null,默认值为0,但UnitsNet枚举中0无实际意义);尝试if (oldUnit != 0)会编译报错,无法直接将泛型枚举与int比较,且UnitsNet各类单位枚举无共同接口,无法添加更具体的泛型约束。
解决方案:两种优雅的实现方式
方式一:引入初始化状态标记
在基类中添加布尔字段标记初始化状态,第一次设置单位时跳过转换逻辑,后续修改正常执行:
public abstract class PropertyBase<TQuantity, TUnit> where TQuantity : IQuantity where TUnit : Enum { public string InputString { get; set; } public TUnit SelectedUnit { get => _selectedUnit; set { var oldUnit = _selectedUnit; _selectedUnit = value; // 仅初始化完成且旧单位为有效单位时,执行转换 if (_isInitialized && IsValidUnit(oldUnit)) { ConvertInputString(oldUnit, value); } // 首次设置单位后标记初始化完成 if (!_isInitialized) { _isInitialized = true; } } } private TUnit _selectedUnit; // 移除可空修饰,枚举为值类型无需可空 private bool _isInitialized = false; // 初始化状态标记 // 判断单位是否在可用列表中(排除无效的0值) private bool IsValidUnit(TUnit unit) { return AvailableUnits.Contains(unit); } // 原有成员保持不变 protected abstract TUnit[] AvailableUnits { get; } protected abstract TQuantity FromValue(double value, TUnit unit); protected abstract double As(TQuantity quantity, TUnit unit); private void ConvertInputString(TUnit oldUnit, TUnit newUnit) { //解析并转换InputString中的值 } protected List<double> ParseInputValues(string input) { //... } }
方式二:利用泛型相等比较器判断默认值
通过EqualityComparer<TUnit>.Default处理泛型枚举的默认值(0)比较,结合可用单位列表检查,确保仅在有效单位切换时执行转换:
public abstract class PropertyBase<TQuantity, TUnit> where TQuantity : IQuantity where TUnit : Enum { public string InputString { get; set; } public TUnit SelectedUnit { get => _selectedUnit; set { var oldUnit = _selectedUnit; _selectedUnit = value; // 旧单位不是默认值(0),且新单位为有效单位时执行转换 if (!EqualityComparer<TUnit>.Default.Equals(oldUnit, default) && IsValidUnit(value)) { ConvertInputString(oldUnit, value); } } } private TUnit _selectedUnit; // 移除可空修饰 // 判断单位是否在可用列表中 private bool IsValidUnit(TUnit unit) { return AvailableUnits.Contains(unit); } // 原有成员保持不变 protected abstract TUnit[] AvailableUnits { get; } protected abstract TQuantity FromValue(double value, TUnit unit); protected abstract double As(TQuantity quantity, TUnit unit); private void ConvertInputString(TUnit oldUnit, TUnit newUnit) { //解析并转换InputString中的值 } protected List<double> ParseInputValues(string input) { //... } }
方案优势
- 两种方式均无需修改UnitsNet原有代码,也不需要额外的接口约束,完美适配现有架构。
- 方式一逻辑直观,明确区分首次初始化与后续修改场景;方式二利用.NET内置工具类处理泛型比较,避免编译错误的同时保持代码简洁。
内容的提问来源于stack exchange,提问作者K.S.
相关产品推荐
相关产品推荐

