在C#中如何声明仅支持两种类型的属性?
实现方案:自定义泛型包装类 + PropertyGrid类型转换器
1. 定义泛型包装类
先写一个泛型类,把目标值和Expression实例统一包装起来,同时通过隐式转换让赋值更直观:
public class ValueOrExpression<T> { public T? Value { get; private set; } public Expression? Expression { get; private set; } public bool IsExpression => Expression != null; // 隐式转换:直接赋值目标类型(比如bool/int) public static implicit operator ValueOrExpression<T>(T value) { return new ValueOrExpression<T> { Value = value }; } // 隐式转换:直接赋值Expression实例 public static implicit operator ValueOrExpression<T>(Expression expression) { // 可选:在这里校验Expression的返回类型是否匹配T,避免无效赋值 return new ValueOrExpression<T> { Expression = expression }; } // 运行时求值方法,获取最终的T类型结果 public T Evaluate() { if (IsExpression) { return (T)Expression.Evaluate(); // 假设你的Expression类有Evaluate()方法返回object } return Value ?? throw new InvalidOperationException("未设置有效值或表达式"); } }
2. 为PropertyGrid写自定义类型转换器
WinForms的PropertyGrid依赖TypeConverter来处理类型的编辑和显示,我们需要为每个ValueOrExpression<T>写对应的转换器,以bool为例:
public class BoolValueOrExpressionConverter : TypeConverter { // 允许从字符串、bool、Expression类型转换 public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) { return sourceType == typeof(string) || sourceType == typeof(bool) || sourceType == typeof(Expression) || base.CanConvertFrom(context, sourceType); } // 把输入内容转换成ValueOrExpression<bool> public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) { if (value is string inputStr) { if (inputStr.Equals("true", StringComparison.OrdinalIgnoreCase)) return new ValueOrExpression<bool>(true); if (inputStr.Equals("false", StringComparison.OrdinalIgnoreCase)) return new ValueOrExpression<bool>(false); // 约定用户输入的表达式前缀为"Expr:",或者直接把非bool字符串当作表达式 var exprText = inputStr.StartsWith("Expr:", StringComparison.OrdinalIgnoreCase) ? inputStr.Substring(5) : inputStr; return new ValueOrExpression<bool>(new Expression(exprText)); } if (value is bool b) return new ValueOrExpression<bool>(b); if (value is Expression expr) return new ValueOrExpression<bool>(expr); return base.ConvertFrom(context, culture, value); } // 把包装类转换成PropertyGrid显示的字符串 public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (destinationType == typeof(string) && value is ValueOrExpression<bool> voe) { return voe.IsExpression ? $"Expr: {voe.Expression.ExpressionText}" : voe.Value.ToString(); } return base.ConvertTo(context, culture, value, destinationType); } // 给PropertyGrid提供下拉选项:true/false/Expression public override bool GetStandardValuesSupported(ITypeDescriptorContext? context) { return true; } public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext? context) { return new StandardValuesCollection(new object[] { true, false, "Expression" }); } // 允许用户输入自定义表达式(非下拉选项的值) public override bool GetStandardValuesExclusive(ITypeDescriptorContext? context) { return false; } }
同理,你可以为int、double、DateTime等类型编写对应的IntValueOrExpressionConverter、DoubleValueOrExpressionConverter,逻辑类似,只是调整类型判断和标准值。
3. 在实体属性上应用
把原来的object类型属性替换成ValueOrExpression<T>,并标记对应的TypeConverter:
public class AppSettings { [TypeConverter(typeof(BoolValueOrExpressionConverter))] public ValueOrExpression<bool> EnableAutoSave { get; set; } = false; [TypeConverter(typeof(IntValueOrExpressionConverter))] public ValueOrExpression<int> MaxRetryCount { get; set; } = 3; }
核心优势
- 强类型约束:编译时就能阻止无关类型赋值,避免原object类型的类型安全问题
- 适配PropertyGrid交互:通过TypeConverter实现了用户需要的下拉选择(true/false/Expression)+ 自定义表达式输入,不用拆成两个属性破坏体验
- 用法自然:支持隐式转换,赋值时直接写
EnableAutoSave = true;或EnableAutoSave = new Expression("SomeExpression");,和原object属性的用法几乎一致
内容的提问来源于stack exchange,提问作者Mark Roworth
相关产品推荐
相关产品推荐

