如何在特性中指定UnitsNet类型并实现MyDisplayAttribute?
实现MyDisplayAttribute以支持UnitsNet单位转换与展示配置
问题背景
我有一个存储计算所得double类型值的简单模型类,所有属性的基准单位由我控制(比如长度单位固定为毫米、体积单位固定为立方毫米)。模型类代码如下:
public class Model { [MyDisplay(UnitsNet.Volume)] public double Volume { get; set; } [MyDisplay(UnitsNet.Length)] public double Length { get; set; } }
我希望通过特性标注属性的单位类型,让展示应用能在运行时为用户提供单位选择功能(比如把所有长度转为英寸、所有体积转为立方英尺)。已知可以在运行时获取Model类的属性、读取特性值并转换模型值,但不清楚MyDisplayAttribute的具体实现方法。
解决方案
1. 实现MyDisplayAttribute类
由于特性的构造函数参数只能是编译时常量,我们需要用UnitsNet提供的QuantityType枚举来存储属性对应的单位类型,而非直接传递Quantity实例。
using System; using UnitsNet; [AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)] public class MyDisplayAttribute : Attribute { // 存储对应的UnitsNet数量类型 public QuantityType QuantityType { get; } public MyDisplayAttribute(QuantityType quantityType) { QuantityType = quantityType; } }
2. 调整Model类的特性标注
将特性参数替换为QuantityType枚举的具体值,匹配UnitsNet的类型定义:
public class Model { [MyDisplay(QuantityType.Volume)] public double Volume { get; set; } // 基准单位:立方毫米 [MyDisplay(QuantityType.Length)] public double Length { get; set; } // 基准单位:毫米 }
3. 运行时属性读取与单位转换工具类
实现一个工具类,负责读取特性信息、获取用户配置的目标单位,并完成基准值到目标单位的转换:
using System; using System.Collections.Generic; using System.Reflection; using UnitsNet; using UnitsNet.Units; public class UnitConversionHelper { // 模拟用户选择的单位配置:Key为数量类型,Value为目标单位枚举 private static readonly Dictionary<QuantityType, Enum> _userSelectedUnits = new() { { QuantityType.Length, LengthUnit.Inch }, { QuantityType.Volume, VolumeUnit.CubicFoot } }; /// <summary> /// 将模型属性的基准值转换为用户选择的单位并返回带单位的字符串 /// </summary> public static string GetDisplayValue(object model, PropertyInfo property) { var displayAttr = property.GetCustomAttribute<MyDisplayAttribute>(); if (displayAttr == null) return property.GetValue(model)?.ToString() ?? string.Empty; var quantityType = displayAttr.QuantityType; if (!_userSelectedUnits.TryGetValue(quantityType, out var targetUnit)) return property.GetValue(model)?.ToString() ?? string.Empty; var baseValue = (double)property.GetValue(model)!; // 根据数量类型执行对应转换逻辑 return quantityType switch { QuantityType.Length => Length.FromMillimeters(baseValue).ToUnit((LengthUnit)targetUnit).ToString(), QuantityType.Volume => Volume.FromCubicMillimeters(baseValue).ToUnit((VolumeUnit)targetUnit).ToString(), // 可扩展支持更多UnitsNet数量类型 _ => baseValue.ToString() }; } /// <summary> /// 获取指定数量类型下的所有可用单位列表,用于用户选择界面 /// </summary> public static IEnumerable<Enum> GetAvailableUnits(QuantityType quantityType) { return UnitAbbreviationsCache.Default.GetAllUnits(quantityType); } }
4. 使用示例
// 初始化模型实例 var model = new Model { Length = 100, // 100毫米 Volume = 1000000 // 1000000立方毫米 = 1立方分米 }; // 遍历模型属性并输出转换后的展示值 var properties = typeof(Model).GetProperties(); foreach (var prop in properties) { var displayValue = UnitConversionHelper.GetDisplayValue(model, prop); Console.WriteLine($"{prop.Name}: {displayValue}"); } // 输出结果: // Length: 3.93700787401575 in // Volume: 0.0353146667214886 ft³
关键说明
- 特性参数限制:特性仅支持编译时常量作为参数,因此必须使用
QuantityType枚举而非UnitsNet的Quantity实例。 - 基准单位约定:转换逻辑严格依赖模型属性的基准单位定义(如Length对应毫米),需确保代码与约定一致。
- 扩展性:如需支持更多UnitsNet数量类型,只需在
switch分支中添加对应转换逻辑即可。
内容的提问来源于stack exchange,提问作者Krazykraut
相关产品推荐
相关产品推荐

