类型化语言中产品聚合存储多类型属性的实现方案咨询
EAV模式下类型化产品属性的存储实现方案
针对你遇到的Product类无法统一存储不同类型ProductAttributeAssignment子类的问题,这里提供几种兼顾类型安全和业务扩展性的实现方案:
方案1:非泛型接口+泛型基类(推荐)
通过定义非泛型接口实现统一存储,同时用泛型基类保留类型信息,既满足List存储需求,又能在使用时获取强类型属性值。
代码实现
// 非泛型接口,用于统一存储 public interface IProductAttribute { AttributeDefinition Attribute { get; } } // 泛型抽象基类,继承非泛型接口 public abstract class ProductAttributeAssignment<T> : IProductAttribute { public AttributeDefinition Attribute { get; set; } public abstract T Value { get; set; } } // 具体属性子类 public class DecimalProductAttribute : ProductAttributeAssignment<double> { public override double Value { get; set; } } public class VarcharProductAttribute : ProductAttributeAssignment<string> { public override string Value { get; set; } } public class TextProductAttribute : ProductAttributeAssignment<string> { public override string Value { get; set; } } // Product类定义 public class Product { public string Name { get; set; } // 用非泛型接口的List统一存储所有属性 public List<IProductAttribute> Attributes { get; set; } = new List<IProductAttribute>(); // 类型安全的属性获取方法,避免强制转换 public T? GetAttributeValue<T>(string attributeName) { var attribute = Attributes .OfType<ProductAttributeAssignment<T>>() .FirstOrDefault(a => a.Attribute.attributeName == attributeName); return attribute?.Value; } // 针对常用类型的快捷方法 public double? GetDecimalAttributeValue(string attributeName) { return GetAttributeValue<double>(attributeName); } public string? GetStringAttributeValue(string attributeName) { return GetAttributeValue<string>(attributeName); } }
优势
- 完全保留属性的原生类型,支持数值计算、范围搜索等场景
- 通过泛型和类型安全的获取方法,避免运行时类型转换错误
- 扩展性强,新增属性类型只需继承
ProductAttributeAssignment<T>即可
方案2:字典存储+类型转换封装
如果不想引入过多接口和泛型,可以用Dictionary<string, object>存储属性值,结合AttributeDefinition的类型标记做封装,确保类型安全。
代码实现
public class Product { public string Name { get; set; } // 存储属性定义和对应值的字典 public Dictionary<AttributeDefinition, object> Attributes { get; set; } = new Dictionary<AttributeDefinition, object>(); // 设置属性值的类型安全方法 public void SetAttributeValue(AttributeDefinition definition, object value) { // 校验类型匹配,避免错误赋值 if (!IsValueMatchingType(definition, value)) { throw new ArgumentException($"Value type does not match attribute {definition.attributeName}'s data type"); } Attributes[definition] = value; } // 获取属性值的类型安全方法 public T? GetAttributeValue<T>(AttributeDefinition definition) { if (Attributes.TryGetValue(definition, out var value)) { return (T)value; } return default; } // 辅助方法:校验值与属性类型是否匹配 private bool IsValueMatchingType(AttributeDefinition definition, object value) { return definition.dataType switch { DataType.Numeric => value is double, DataType.Varchar => value is string, DataType.Text => value is string, _ => false }; } } // 补充DataType枚举定义 public enum DataType { Numeric, Varchar, Text }
优势
- 实现简单,代码量少
- 直接通过字典关联属性定义和值,逻辑清晰
劣势
- 依赖object类型,虽然有封装校验,但仍存在一定的类型转换风险
方案3:访问者模式(适合复杂操作场景)
如果后续需要频繁对不同类型属性执行统一操作(比如批量导出、范围过滤),可以使用访问者模式,将属性操作与属性类型解耦。
代码实现
// 访问者接口 public interface IProductAttributeVisitor { void Visit(DecimalProductAttribute attribute); void Visit(VarcharProductAttribute attribute); void Visit(TextProductAttribute attribute); } // 修改抽象基类,添加Accept方法 public abstract class ProductAttributeAssignment { public AttributeDefinition Attribute { get; set; } public abstract void Accept(IProductAttributeVisitor visitor); } // 具体属性子类实现Accept方法 public class DecimalProductAttribute : ProductAttributeAssignment { public double Value { get; set; } public override void Accept(IProductAttributeVisitor visitor) { visitor.Visit(this); } } public class VarcharProductAttribute : ProductAttributeAssignment { public string Value { get; set; } public override void Accept(IProductAttributeVisitor visitor) { visitor.Visit(this); } } // Product类 public class Product { public string Name { get; set; } public List<ProductAttributeAssignment> Attributes { get; set; } = new List<ProductAttributeAssignment>(); // 执行访问者操作 public void ExecuteVisitor(IProductAttributeVisitor visitor) { foreach (var attribute in Attributes) { attribute.Accept(visitor); } } } // 示例访问者:筛选数值属性大于指定值的属性 public class NumericRangeFilterVisitor : IProductAttributeVisitor { private readonly double _minValue; public List<DecimalProductAttribute> MatchingAttributes { get; } = new List<DecimalProductAttribute>(); public NumericRangeFilterVisitor(double minValue) { _minValue = minValue; } public void Visit(DecimalProductAttribute attribute) { if (attribute.Value > _minValue) { MatchingAttributes.Add(attribute); } } public void Visit(VarcharProductAttribute attribute) { } public void Visit(TextProductAttribute attribute) { } }
优势
- 完美支持不同类型属性的差异化操作,扩展新操作只需新增访问者类
- 避免大量if-else类型判断,代码更整洁
劣势
- 新增属性类型时需要修改访问者接口和所有实现类,仅适合属性类型相对稳定的场景
内容的提问来源于stack exchange,提问作者user2190492
相关产品推荐
相关产品推荐

