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

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.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 22:03:14