C#中多类型集合多初始化器的优化实现方案咨询
问题描述
我定义了如下DataSet类:
class DataSet { List<int> IntList; List<int> IntList2; List<string> StringList; // ...还有很多其他不同类型的列表 }
目前需要编写多个初始化器类,例如:
class InitializerLocalFileSync { void Load(DataSet d) { LoadSync(d.IntList); LoadSync(d.IntList2); LoadSync(d.StringList); // ...还有大量初始化调用 } } class InitializerLocalFileAsync { async Task Load(DataSet d) { await LoadAsync(d.IntList); await LoadAsync(d.IntList2); await LoadAsync(d.StringList); // ...同样有大量初始化调用 } } class InitializerDownloadFromWWW // 还有这类不同数据源的初始化器 { }
当前实现能正常运行,但存在大量重复代码。每当DataSet的成员增删时,所有初始化器都要修改,而DataSet变更频繁,维护成本太高,求更优实现方式?
解决方案
方案1:反射+泛型统一处理核心逻辑
利用反射自动遍历DataSet中所有列表类型的成员,避免手动逐个调用初始化方法。每个初始化器只需要实现对应类型的加载逻辑,新增DataSet成员时无需修改初始化器代码。
首先封装通用的反射遍历逻辑到抽象基类:
public abstract class DataSetInitializer { protected void ProcessAllLists<T>(DataSet dataSet, Action<T> loadAction) where T : class { // 获取DataSet中所有List<T>类型的私有实例字段 var listFields = typeof(DataSet).GetFields(BindingFlags.Instance | BindingFlags.NonPublic) .Where(f => f.FieldType.IsGenericType && f.FieldType.GetGenericTypeDefinition() == typeof(List<>)); foreach (var field in listFields) { var listInstance = field.GetValue(dataSet) as T; if (listInstance != null) { loadAction(listInstance); } } } protected async Task ProcessAllListsAsync<T>(DataSet dataSet, Func<T, Task> loadAction) where T : class { var listFields = typeof(DataSet).GetFields(BindingFlags.Instance | BindingFlags.NonPublic) .Where(f => f.FieldType.IsGenericType && f.FieldType.GetGenericTypeDefinition() == typeof(List<>)); foreach (var field in listFields) { var listInstance = field.GetValue(dataSet) as T; if (listInstance != null) { await loadAction(listInstance); } } } }
然后改造各个初始化器:
class InitializerLocalFileSync : DataSetInitializer { public void Load(DataSet d) { ProcessAllLists<object>(d, list => { if (list is List<int> intList) LoadSync(intList); else if (list is List<string> strList) LoadSync(strList); // 新增类型时仅需添加对应分支 }); } private void LoadSync(List<int> list) { // 同步加载int列表的逻辑 } private void LoadSync(List<string> list) { // 同步加载string列表的逻辑 } } class InitializerLocalFileAsync : DataSetInitializer { public async Task Load(DataSet d) { await ProcessAllListsAsync<object>(d, async list => { if (list is List<int> intList) await LoadAsync(intList); else if (list is List<string> strList) await LoadAsync(strList); }); } private Task LoadAsync(List<int> list) { // 异步加载int列表的逻辑 return Task.CompletedTask; } private Task LoadAsync(List<string> list) { // 异步加载string列表的逻辑 return Task.CompletedTask; } } class InitializerDownloadFromWWW : DataSetInitializer { public async Task Load(DataSet d) { await ProcessAllListsAsync<object>(d, async list => { if (list is List<int> intList) await DownloadList(intList); else if (list is List<string> strList) await DownloadList(strList); }); } private Task DownloadList(List<int> list) { // 从网络下载int列表数据的逻辑 return Task.CompletedTask; } private Task DownloadList(List<string> list) { // 从网络下载string列表数据的逻辑 return Task.CompletedTask; } }
方案2:自定义属性标记需初始化的成员
如果不想处理DataSet中所有列表,只想初始化特定成员,可以给目标成员加自定义属性,反射时仅处理带标记的字段:
首先定义自定义属性:
[AttributeUsage(AttributeTargets.Field)] public class InitializeMemberAttribute : Attribute { }
修改DataSet标记需要初始化的成员:
class DataSet { [InitializeMember] List<int> IntList; [InitializeMember] List<int> IntList2; [InitializeMember] List<string> StringList; // 不需要初始化的列表不加此属性 }
修改抽象基类的遍历逻辑,过滤出带标记的字段:
protected void ProcessMarkedLists<T>(DataSet dataSet, Action<T> loadAction) where T : class { var listFields = typeof(DataSet).GetFields(BindingFlags.Instance | BindingFlags.NonPublic) .Where(f => f.FieldType.IsGenericType && f.FieldType.GetGenericTypeDefinition() == typeof(List<>) && f.GetCustomAttribute<InitializeMemberAttribute>() != null); foreach (var field in listFields) { var listInstance = field.GetValue(dataSet) as T; if (listInstance != null) { loadAction(listInstance); } } }
后续初始化器的使用逻辑和方案1一致,这样可以精准控制需要初始化的成员。
方案3:封装DataSet的列表为可枚举集合(允许修改DataSet时使用)
如果可以修改DataSet结构,直接把所有需要初始化的列表整理成可枚举集合,完全避免反射,性能更优:
class DataSet { List<int> IntList; List<int> IntList2; List<string> StringList; // 暴露所有需要初始化的列表 public IEnumerable<object> GetAllLists() { yield return IntList; yield return IntList2; yield return StringList; // 新增列表时仅需在此添加一行 } }
简化后的初始化器:
class InitializerLocalFileSync { public void Load(DataSet d) { foreach (var list in d.GetAllLists()) { if (list is List<int> intList) LoadSync(intList); else if (list is List<string> strList) LoadSync(strList); } } // 加载方法同前 }
这个方案需要维护GetAllLists方法,但相比修改所有初始化器,维护成本大幅降低。
内容的提问来源于stack exchange,提问作者leffe
相关产品推荐
相关产品推荐

