C#中结构相同的A、B类如何重构重复的GernerateStuff方法
实现方案
核心思路是使用泛型方法+接口约束,不需要返回弱类型的接口实例,既能完全复用公共逻辑,又能保证返回强类型的A、B对象,不需要额外类型转换,完全满足不合并A、B两个独立类的约束。
前置准备
先定义语义明确的公共接口,让A、B类实现该接口即可:
public interface IStuffEntity { string Stuff{ get; set; } DateTime Created { get; set; } DateTime Expires { get; set; } } public class A : IStuffEntity { public string Stuff{ get; set; } public DateTime Created { get; set; } public DateTime Expires { get; set; } } public class B : IStuffEntity { public string Stuff{ get; set; } public DateTime Created { get; set; } public DateTime Expires { get; set; } }
注:原代码中接口命名
IInterface过于宽泛,不符合C#命名规范,可根据实际场景替换为更有业务含义的名称,不影响核心逻辑。
核心通用方法实现
原代码标记了async但方法内部没有任何异步等待操作,属于多余标记,会产生编译警告,以下实现移除了不必要的async关键字,如果后续确实要加入异步IO等逻辑,再补回async/await即可。
public T GenerateStuff<T>(int expireDays = 15) where T : IStuffEntity, new() { using var randomNumberGenerator = RandomNumberGenerator.Create(); var randomBytes = new byte[64]; var now = DateTime.UtcNow; randomNumberGenerator.GetBytes(randomBytes); return new T { Stuff = Convert.ToBase64String(randomBytes), Created = now, Expires = now.AddDays(expireDays) }; }
两个泛型约束的作用:
where T : IStuffEntity:保证传入的类型都拥有Stuff、Created、Expires三个属性可以直接赋值,不会出现编译错误where T : new():保证传入的类型拥有无参构造函数,可以直接在方法内部创建实例,不需要额外传入构造逻辑
调用方式
调用时指定泛型类型即可,返回值为强类型,可直接使用:
- 生成A类实例(使用默认15天有效期):
A aInstance = GenerateStuff<A>(); - 生成B类实例(指定10天有效期):
B bInstance = GenerateStuff<B>(10);
兜底兼容方案
如果后续A、B类修改了定义,没有无参构造函数(比如新增了必须传入的初始化参数),可以改用传入工厂委托的方式实现,不需要new()约束:
public T GenerateStuff<T>(Func<T> instanceFactory, int expireDays = 15) where T : IStuffEntity { if (instanceFactory == null) throw new ArgumentNullException(nameof(instanceFactory)); using var randomNumberGenerator = RandomNumberGenerator.Create(); var randomBytes = new byte[64]; var now = DateTime.UtcNow; randomNumberGenerator.GetBytes(randomBytes); var instance = instanceFactory(); instance.Stuff = Convert.ToBase64String(randomBytes); instance.Created = now; instance.Expires = now.AddDays(expireDays); return instance; }
对应调用方式:
// 适配需要传入构造参数的场景 A aInstance = GenerateStuff(() => new A(/* 传入A类需要的初始化参数 */), 15); B bInstance = GenerateStuff(() => new B(/* 传入B类需要的初始化参数 */), 10);
不推荐直接返回IInterface类型的原因
如果把方法返回值设为IInterface,调用方拿到结果后如果要访问A/B类独有的属性、方法,必须手动执行强制类型转换,既增加了冗余代码,又存在转换失败的运行时风险,泛型方案完全规避了这个问题,是这类场景的标准实现方式。
内容的提问来源于stack exchange,提问作者Sharif Mamun
相关产品推荐
相关产品推荐

