在我的 C# 应用程序中,我有一个抽象类,它是 UnitsNet 库的包装器,用于对密切相关的属性(例如用户选择的数值和该值的单位)进行分组,以限制可用的测量单位,并且还处理来自用户的复杂输入(例如多个数字输入,例如“5,8,10:1:20”)。这是基类和扩展它的类之一:
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) //This check doesn't work
{
//This code should only be run if oldUnit is not null or 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)
{
//Parses and converts values in 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库的Enum。
这个想法是,当
InputPropertyAngle
被实例化,并且 SelectedUnit
在构造函数中设置为默认单位时,ConvertInputString
不应该运行。这是通过检查 oldUnit
是否为空 (if (oldUnit != null)
) 来完成的。但是,由于 oldUnit
是 TUnit
(通用枚举),因此它不能为空。在运行时,它假定值为 0。
幸运的是,在UnitsNet库中,在
AngleUnit
枚举中,0不代表任何有意义的值。所以我想我可以用 if (oldUnit != 0)
替换该检查,但编译器说 Cannot apply operator '!=' to operands of type 'TUnit' and 'int'
。不幸的是,我不能将 TUnit
限制为比 Enum 更具体的东西,因为 AngleUnit
、LengthUnit
、MassUnit
和来自 UnitsNet 的其他枚举没有任何通用接口(据我所知,枚举不能有根本就是这样)。
解决这个问题以使空检查成为可能的最优雅的方法是什么?
您可以将
TUnit
限制为 struct
并删除可空性并使用 default
(我将其移至虚拟属性,以便继承者可以根据需要重新定义 Default
):
public abstract class PropertyBase<TQuantity, TUnit> where TQuantity : IQuantity where TUnit : struct, Enum
{
protected virtual TUnit Default => default;
public TUnit SelectedUnit
{
get => _selectedUnit;
set
{
var oldUnit = _selectedUnit;
_selectedUnit = value;
if (!oldUnit.Equals(Default))
{
//This code should only be run if oldUnit is not null or 0
ConvertInputString(oldUnit, value);
}
}
}
或者使用可空性:
public abstract class PropertyBase<TQuantity, TUnit> where TQuantity : IQuantity where TUnit : struct, Enum
{
public TUnit? SelectedUnit
{
get => _selectedUnit;
set
{
var oldUnit = _selectedUnit;
_selectedUnit = value;
if (oldUnit is not null && value is not null)
{
//This code should only be run if oldUnit is not null or 0
ConvertInputString(oldUnit.Value, value.Value);
}
}
}
尝试将
TEnum
限制为 struct 和 Enum。
public abstract class PropertyBase<TQuantity, TUnit>
where TQuantity : IQuantity
where TUnit : struct, Enum
{
public TUnit? SelectedUnit
{
get => _selectedUnit;
set
{
var oldUnit = _selectedUnit;
_selectedUnit = value;
if (oldUnit is Enum nonNull)
{
}
else
{
// Code will not run
}
}
}
// You couldn't do this before but now you should be ablt to.
private TUnit? _selectedUnit = null;
.
.
.
}