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

如何在特性中指定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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 16:01:48