You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 03:35:16