如何让Export类的ExportParts适配任意WebData派生类(禁用反射)
嘿,这个需求我太懂了——既要让ExportParts适配所有WebData子类,又不想靠高开销的反射拿类名对吧?之前做类似导出功能时踩过不少坑,给你分享几个亲测好用的方案,都是编译时就能搞定、完全没反射开销的:
方案1:通用类型名称缓存(零侵入现有代码)
这个方案不需要修改你的WebData基类或者任何派生类,靠泛型静态类的编译时特性来缓存类名:
// 全局通用的类型名称缓存类 public static class TypeNameCache<T> { // 编译时就确定T的名称,静态字段只会初始化一次 public static readonly string Name = typeof(T).Name; } // 你的Export类里这么用 public class Export { public void ExportParts<T>(T data) where T : WebData { // 直接拿缓存好的类名,几乎零开销 string className = TypeNameCache<T>.Name; // 后续导出逻辑... } }
这里的typeof(T).Name是编译时解析的,不是运行时反射操作,静态字段初始化一次后就一直可用,性能拉满,而且不管你加多少WebData的派生类,都不用改这个缓存类,直接用就行。
方案2:基类抽象属性(编译时强制校验)
如果你能修改WebData基类,那这个方案最稳妥——给基类加个抽象属性,让每个派生类自己返回类名,完全没有运行时开销:
public abstract class WebData { // 强制所有派生类实现这个属性 public abstract string ClassName { get; } } // 派生类里直接用nameof,编译时就确定值 public class BikeModel : WebData { public override string ClassName => nameof(BikeModel); } // Export类里直接调用 public void ExportParts<T>(T data) where T : WebData { string className = data.ClassName; // ... }
这个方案的好处是编译时就能检查所有派生类有没有正确实现类名,不会出现运行时的意外,而且完全没反射相关的操作,性能最优。
方案3:奇异递归模板模式(CRTP)
如果不想让每个派生类都写重复的属性代码,可以用CRTP让基类提前知道派生类的类型:
// 基类改成泛型,约束T是自己的派生类 public abstract class WebData<T> where T : WebData<T> { // 编译时就拿到派生类的类名,存在静态字段里 public static readonly string ClassName = typeof(T).Name; } // 派生类继承时指定自身类型 public class BikeModel : WebData<BikeModel> { // 啥都不用写,自动继承基类的静态字段 } // Export类里这么用 public void ExportParts<T>(T data) where T : WebData<T> { string className = T.ClassName; // ... }
这个方案的好处是派生类不用写额外代码,基类统一处理,但需要修改基类为泛型,可能会影响现有代码的结构,适合重构时使用。
小提醒
如果你的代码里是像WebData data = new BikeModel();这种基类引用的情况,调用ExportParts时一定要明确指定泛型类型,比如export.ExportParts<BikeModel>(data),不然编译时T会被推断为WebData,拿到的就是基类的类名了。如果是运行时才知道具体类型的场景,那可能绕不开data.GetType().Name,但这个其实是轻量级的反射操作,开销远低于遍历属性那种;要是实在不能接受,就得在代码里提前用模式匹配把基类实例转成具体派生类再调用泛型方法啦。
内容的提问来源于stack exchange,提问作者ppk

