You need to enable JavaScript to run this app. C#中如何实现根据枚举参数动态限定类的可用属性?
优惠活动
大模型
产品
解决方案
定价
更多

C#中如何实现根据枚举参数动态限定类的可用属性?

原实现的核心问题

原设计采用平铺式属性结构,将不同取值规则下的专属属性全部放置在同一个类中,没有建立属性和类型的绑定关系,既容易产生类型不匹配的无效数据(比如Range类型下给Presets赋值),也无法从根本上阻止调用方访问不属于当前类型的属性。

最优实现:基于继承的可辨识结构设计

C#没有原生的可辨识联合(Discriminated Union)语法,可以通过抽象基类+派生类的方式实现同等效果,从编译期就约束属性的合法访问范围,满足“特定类型下才存在对应属性”的需求。

实现步骤

  1. 提取所有类型共有的基础属性到抽象基类
public enum ValueType
{
    Range,
    Preset
}

public abstract class Register
{
    // 所有寄存器通用的基础属性
    public string Name { get; set; }
    public string Unit { get; set; }
    public byte ID { get; set; }
    public string Address { get; set; }

    // 保留原有的通用方法
    public ushort GetAddrAsShort()
    {
        return Convert.ToUInt16(Address, 16);
    }

    // 保留原有类型标识能力,由派生类固定返回对应值
    public abstract ValueType RegisterType { get; }
}
  1. 为两种取值规则分别创建派生类,每个类只封装自身合法存在的专属属性
  • Range类型寄存器:
public class RangeRegister : Register
{
    // 仅Range类型可访问的取值范围属性
    public byte MaxValue { get; set; }
    public byte MinValue { get; set; }

    public override ValueType RegisterType => ValueType.Range;
}
  • Preset类型寄存器:
public class PresetRegister : Register
{
    // 仅Preset类型可访问的预设值字典
    public Dictionary<byte, string> Presets { get; set; }

    public override ValueType RegisterType => ValueType.Preset;
}

方案优势

  • 编译期强约束:要访问MaxValue、MinValue,必须先将对象显式转换为RangeRegister类型;要访问Presets,必须转换为PresetRegister类型,从语法层面彻底杜绝跨类型访问属性的逻辑错误
  • 无冗余内存开销:每个类只存储自身需要的字段,不会出现非对应类型下的属性冗余占位
  • 扩展灵活:后续新增取值规则类型时,只需新增对应派生类即可,无需修改原有核心类的代码
兼容旧代码的过渡方案

如果暂时无法重构原有调用逻辑,可在原类结构的属性访问逻辑中加入合法性校验,运行时拦截非法访问:

class Register
{
    public string Name { get; set; }
    public string Unit { get; set; }
    public byte ID { get; set; }
    public ValueType type { get; set; }  
    public string Address { get; set; }

    // 私有字段存储专属属性值,不直接对外暴露
    private byte _maxValue;
    private byte _minValue;
    private Dictionary<byte, string> _presets;

    public byte MaxValue
    {
        get => type == ValueType.Range 
            ? _maxValue 
            : throw new InvalidOperationException("当前为Preset类型寄存器,不存在MaxValue属性");
        set
        {
            if (type != ValueType.Range) 
                throw new InvalidOperationException("仅Range类型寄存器支持设置MaxValue");
            _maxValue = value;
        }
    }

    public byte MinValue
    {
        get => type == ValueType.Range 
            ? _minValue 
            : throw new InvalidOperationException("当前为Preset类型寄存器,不存在MinValue属性");
        set
        {
            if (type != ValueType.Range) 
                throw new InvalidOperationException("仅Range类型寄存器支持设置MinValue");
            _minValue = value;
        }
    }

    public Dictionary<byte, string> Presets
    {
        get => type == ValueType.Preset 
            ? _presets 
            : throw new InvalidOperationException("当前为Range类型寄存器,不存在Presets属性");
        set
        {
            if (type != ValueType.Preset) 
                throw new InvalidOperationException("仅Preset类型寄存器支持设置Presets");
            _presets = value;
        }
    }

    public ushort GetAddrAsShort()
    {
        return Convert.ToUInt16(Address, 16);
    }
}

enum ValueType
{
    Range,
    Preset
}

该方案的合法性校验为运行时检查,错误只有在代码运行到对应逻辑时才会暴露,可靠性弱于编译期约束的派生类方案,仅适合短期兼容旧代码场景使用。


内容的提问来源于stack exchange,提问作者Jacob Petersen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.26 03:06:03