如何在C#中实现带特性的属性复用(替代多继承)
可行的实现方案
方案1:单基类整合(最简单直接)
把需要复用的Name、Address属性合并到一个基类里,所有需要这些属性的类直接继承这个基类,特性只需要写一次:
public class BaseCommon { [StringLength(100), MaxLength(100)] public string Name { get; set; } [StringLength(100), MaxLength(100)] public string Address { get; set; } } public class ClassA : BaseCommon { public string City { get; set; } } public class ClassB : BaseCommon { public string Country { get; set; } }
如果原本想把Name和Address拆分到不同逻辑组,这个方案可能不够灵活,但胜在简单,完全符合C#的继承规则,特性也只维护一次。
方案2:组合模式(保持属性分组)
如果必须把Name和Address拆分到不同的逻辑类里,用组合代替继承:先定义包含属性和特性的独立类,再在业务类里持有这些类的实例,通过转发属性暴露出去,特性只在独立类里写一次:
public class NameHolder { [StringLength(100), MaxLength(100)] public string Name { get; set; } } public class AddressHolder { [StringLength(100), MaxLength(100)] public string Address { get; set; } } public class ClassA { private readonly NameHolder _nameHolder = new NameHolder(); private readonly AddressHolder _addressHolder = new AddressHolder(); // 转发Name属性,复用NameHolder里的特性 public string Name { get => _nameHolder.Name; set => _nameHolder.Name = value; } // 转发Address属性,复用AddressHolder里的特性 public string Address { get => _addressHolder.Address; set => _addressHolder.Address = value; } public string City { get; set; } }
注意:如果你的业务需要通过反射读取ClassA的Name属性上的特性,需要额外处理(比如反射时读取_nameHolder的对应属性),但如果是常规的属性使用或数据验证,这个方案完全可行。
方案3:代码生成器(适合大量类的场景)
如果需要复用的属性组合多、业务类数量大,用代码生成器自动生成重复代码。比如用C#的Source Generator或者T4模板,只需要编写一次属性和特性的模板,就能自动给所有业务类生成对应的属性代码,完全避免手动重复编写。
比如用Source Generator的思路:定义一个标记特性,给需要生成属性的类打上标记,然后生成器自动添加Name、Address属性及对应的特性。
方案4:默认接口方法(C# 8.0+,需注意特性兼容性)
把属性定义在接口里并添加默认实现,然后让业务类实现这些接口。不过接口属性上的特性在反射时需要单独读取,部分数据验证框架可能需要配置支持接口特性,所以这个方案要结合业务场景使用:
public interface IHasName { [StringLength(100), MaxLength(100)] string Name { get; set; } } public interface IHasAddress { [StringLength(100), MaxLength(100)] string Address { get; set; } } // 可选:用一个基类统一实现接口,避免每个业务类重复写实现 public class BaseWithCommonProps : IHasName, IHasAddress { public string Name { get; set; } public string Address { get; set; } } public class ClassA : BaseWithCommonProps { public string City { get; set; } }
内容的提问来源于stack exchange,提问作者Davide Martelli
相关产品推荐
相关产品推荐

