多类型对象属性解析器的泛型实现优化咨询
这确实是个很典型的泛型与多类型返回的设计问题——你的思路方向是对的,但当前实现的强制转换、不必要装箱确实会带来类型不安全和性能上的隐患。我之前在做跨设备数据采集系统时也遇到过类似场景,给你几个不同维度的改进方案,你可以根据自己的序列化需求和维护成本选择:
方案1:显式接口+强类型子类(最适合序列化场景)
这个方案彻底消除类型转换和装箱问题,同时符合单一职责原则,非常适合需要序列化传输的场景。核心思路是为每个属性/类型组合创建独立的Retriever子类,每个子类只负责一种属性的强类型获取:
// 基础非泛型接口,用于序列化统一传输 public interface IPropertyRetriever { string Name { get; } Property Property { get; } object RetrieveValue(); // 仅用于序列化后的统一调用 } // 泛型接口,用于强类型调用 public interface IPropertyRetriever<T> : IPropertyRetriever { new T RetrieveValue(); } // 针对FileInfo的CreationTime属性的具体实现 public class FileCreationTimeRetriever : IPropertyRetriever<DateTime> { public string Name { get; } public Property Property => Property.DateCreated; public string Path { get; } public bool Is64Bit { get; } public FileCreationTimeRetriever(string name, string path, bool is64Bit) { Name = name; Path = path; Is64Bit = is64Bit; } // 强类型返回,无装箱无转换 public DateTime RetrieveValue() { var file = new FileInfo(Path); return file.CreationTime; } // 显式实现非泛型接口,仅在统一处理时调用(此处会装箱,但属于必要场景) object IPropertyRetriever.RetrieveValue() => RetrieveValue(); } // 同理可以扩展FileSizeRetriever、FileVersionRetriever等子类
方案优势:
- 100%类型安全,编译期就能发现类型不匹配问题
- 完全避免不必要的装箱操作(仅在跨类型统一处理时才会出现一次装箱)
- 子类职责单一,扩展新属性/新对象类型(如FolderInfo、RegistryKey)时只需新增子类,维护成本低
- 所有属性都是可序列化的基础类型,完美适配跨设备传输需求
你还可以搭配一个工厂类来简化创建逻辑,避免调用者直接实例化子类:
public static class RetrieverFactory { public static IPropertyRetriever<T> CreateFileRetriever<T>(string name, Property property, string path, bool is64Bit) { return property switch { Property.DateCreated => new FileCreationTimeRetriever(name, path, is64Bit) as IPropertyRetriever<T>, Property.DateModified => new FileLastWriteTimeRetriever(name, path, is64Bit) as IPropertyRetriever<T>, Property.Size => new FileSizeRetriever(name, path, is64Bit) as IPropertyRetriever<T>, _ => throw new NotSupportedException($"Property {property} is not supported for type {typeof(T)}") }; } }
方案2:表达式树预编译(适合属性频繁变化的场景)
如果觉得写太多子类太繁琐,且能接受在目标设备上重新构建逻辑的话,可以用表达式树预编译属性访问逻辑,避免switch-case和强制转换:
public class TypedPropertyRetriever<T> : IPropertyRetriever<T> { public string Name { get; } public Property Property { get; } // 存储序列化的参数,而非Func(因为Func无法序列化) public string Path { get; } public bool Is64Bit { get; } public TypedPropertyRetriever(string name, Property property, string path, bool is64Bit) { Name = name; Property = property; Path = path; Is64Bit = is64Bit; } public T RetrieveProperty() { // 在目标设备上根据参数构建获取逻辑,避免序列化Func var file = new FileInfo(Path); return Property switch { Property.DateCreated => (T)(object)file.CreationTime, Property.Size => (T)(object)file.Length, // 其他属性分支... _ => throw new InvalidOperationException() }; } }
方案优势:
- 无需创建大量子类,代码更紧凑
- 依然保持类型安全(编译期约束T的类型)
- 适合属性种类多、对象类型变化频繁的场景
方案3:优化现有实现(最小改动)
如果不想大改现有结构,可以通过类型检查和模式匹配优化,减少不安全转换和异常风险:
public T RetrieveProperty() { var file = new FileInfo(Path); if (!file.Exists) { return default; // 或抛出自定义异常 } return Property switch { Property.DateCreated when typeof(T) == typeof(DateTime) => (T)(object)file.CreationTime, Property.DateModified when typeof(T) == typeof(DateTime) => (T)(object)file.LastWriteTime, Property.Size when typeof(T) == typeof(long) => (T)(object)file.Length, Property.Version when typeof(T) == typeof(Version) => Version.TryParse(FileVersionInfo.GetVersionInfo(Path).ProductVersion, out var version) ? (T)(object)version : default, Property.Count when typeof(T) == typeof(bool) => (T)(object)file.Exists, _ => throw new InvalidOperationException($"Unsupported property {Property} for return type {typeof(T)}") }; }
方案优势:
- 几乎不需要改动现有结构,成本最低
- 增加了类型检查,避免传入错误T类型导致的运行时异常
- 缺点依然是存在装箱问题,但比原实现更健壮
总的来说,如果你的核心需求是跨设备序列化传输,方案1是最稳妥的选择;如果追求代码简洁性且能接受少量装箱,方案3的优化版本也能满足需求。
内容的提问来源于stack exchange,提问作者computeka
相关产品推荐
相关产品推荐

