定义子类时如何约束C#基类BaseEntity的TId仅为Guid/int/long类型
C# 原生泛型约束暂不支持直接指定泛型参数只能为某几个特定值类型,我们可以通过两种常见方案实现需求,可根据你的项目运行环境选择:
方案1:全版本兼容的运行时校验
该方案适配所有 .NET 版本,通过泛型类的静态构造函数做类型校验,泛型参数不合法时会在第一次使用该类型时抛出异常:
public abstract class BaseEntity<TId> where TId : struct { // 每个封闭泛型类型只会执行一次静态构造函数 static BaseEntity() { if (typeof(TId) != typeof(Guid) && typeof(TId) != typeof(int) && typeof(TId) != typeof(long)) { throw new NotSupportedException($"不支持的 TId 类型 {typeof(TId).FullName},仅允许使用 Guid、int、long 三种类型"); } } private TId ID; }
如果需要更早的错误提示,可以搭配自定义 Roslyn 分析器实现编译时校验,普通业务场景下该方案已经足够满足需求。
方案2:结合构造函数约束实现编译时报错
该方案可以在编译阶段就拦截绝大多数不合法的泛型参数使用,同时搭配静态构造函数兜底避免隐式类型转换带来的漏判:
public abstract class BaseEntity<TId> where TId : struct { static BaseEntity() { if (typeof(TId) != typeof(Guid) && typeof(TId) != typeof(int) && typeof(TId) != typeof(long)) { throw new NotSupportedException($"不支持的 TId 类型 {typeof(TId).FullName},仅允许使用 Guid、int、long 三种类型"); } } private TId ID; // 仅暴露三个对应允许类型的构造函数,子类必须调用其中之一完成初始化 protected BaseEntity(Guid id) => ID = (TId)(object)id; protected BaseEntity(int id) => ID = (TId)(object)id; protected BaseEntity(long id) => ID = (TId)(object)id; }
使用示例:
// 合法定义 public class UserEntity : BaseEntity<int> { public UserEntity(int id) : base(id) { } } // 编译报错:找不到匹配的基类构造函数 public class GoodsEntity : BaseEntity<short> { public GoodsEntity(short id) : base(id) { } }
注:如果存在short、byte等可以隐式转换为int/long的类型调用构造函数的场景,静态构造函数的校验会兜底抛出异常,避免非法类型被误用。
内容的提问来源于stack exchange,提问作者Mike Lenart
相关产品推荐
相关产品推荐

