如何为实现接口的类提供元信息且无需实例化即可获取?
方案1:使用C# 11+ 接口静态抽象成员(最优,编译期校验)
这是最贴合需求的实现方式,完全规避了Attribute方案的所有问题:
// 接口定义静态抽象成员,强制所有实现类必须提供对应元数据 public interface IMetaInterface<TSelf> where TSelf : IMetaInterface<TSelf> { static abstract string RedisKey { get; } } // 实现类直接定义静态属性即可,不需要加Attribute public class Implementation1 : IMetaInterface<Implementation1> { public static string RedisKey => "IMP1"; public int Id { get; set; } } public class Implementation2 : IMetaInterface<Implementation2> { public static string RedisKey => "IMP2"; public int Id { get; set; } } public class Caching { public string BuildCacheKey<T>(int id) where T : IMetaInterface<T> { // 直接访问静态属性,无反射开销,编译期自动校验实现合法性 return T.RedisKey + id.ToString(); } }
优势:
- 编译期强制校验:如果实现类未提供
RedisKey属性,代码直接编译不通过,不需要额外写任何运行时校验逻辑,完全不存在漏配置的风险 - 元信息和实现类强绑定,定义直观不需要额外关联Attribute
- 无反射开销,性能远高于Attribute方案
- 校验逻辑由编译器原生支持,完全和API层解耦,元数据定义直接下沉到领域层
方案2:泛型元数据存储类(兼容低版本C#)
如果使用的是C# 10及以下版本,不支持接口静态抽象成员,可以用泛型静态类的特性存储元数据,同时把校验逻辑封装到领域层:
public interface IMetaInterface { } // 领域层定义泛型元数据存储类 public static class MetaInfo<T> where T : IMetaInterface { public static string RedisKey { get; } // 第一次访问该类型时自动触发校验和元数据缓存 static MetaInfo() { var attr = (MetaInfoAttribute)Attribute.GetCustomAttribute(typeof(T), typeof(MetaInfoAttribute)); if (attr == null) { throw new InvalidOperationException($"类型 {typeof(T).FullName} 未配置MetaInfoAttribute"); } RedisKey = attr.RedisKey; } } // 原有Attribute和实现类不需要修改 [MetaInfo("IMP1")] public class Implementation1 : IMetaInterface { public int Id { get; set; } } public class Caching { public string BuildCacheKey<T>(int id) where T : IMetaInterface { // 仅第一次调用反射,后续直接读取缓存的元数据 return MetaInfo<T>.RedisKey + id.ToString(); } }
如果需要启动时全量校验,直接在领域层封装校验方法即可,API层只需要调用不需要关心实现:
// 领域层校验方法 public static class MetaInfoValidator { public static void ValidateAllImplementations() { var implementations = AppDomain.CurrentDomain.GetAssemblies() .SelectMany(a => a.GetTypes()) .Where(t => typeof(IMetaInterface).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract); foreach (var implType in implementations) { var metaType = typeof(MetaInfo<>).MakeGenericType(implType); _ = metaType.GetProperty(nameof(MetaInfo<IMetaInterface>.RedisKey))!.GetValue(null); } } }
方案3:源码生成器编译期校验Attribute
如果不想修改现有Attribute的使用习惯,可以写一个C#源码生成器,编译期扫描所有IMetaInterface的实现类,校验是否加了MetaInfoAttribute,没加的直接在编译阶段报错,不需要运行时校验。
内容的提问来源于stack exchange,提问作者IRONicMAN
相关产品推荐
相关产品推荐

