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

如何在不二次迭代的情况下拆分ObservableCollection转换的事件订阅

解决方案

核心思路是利用C#的延迟执行枚举特性,将模型创建、属性映射、事件订阅三个步骤合并到一次遍历中,同时把订阅逻辑从核心转换方法里拆分出来,保持方法简洁性的同时避免二次迭代。

步骤1:拆分核心转换逻辑

先把DataTable到模型的转换逻辑抽成返回IEnumerable<T>的延迟执行方法,只负责创建模型并填充属性:

using System.Data;
using System.Collections.Generic;
using System.ComponentModel;

public static class DataTableExtensions
{
    // 核心模型转换:仅负责从DataRow创建并填充模型
    public static IEnumerable<T> ToModels<T>(this DataTable dataTable) 
        where T : INotifyPropertyChanged, new()
    {
        // 替换为你原有的动态映射逻辑(比如反射、表达式树实现)
        var propertyMap = GetPropertyColumnMap<T>(dataTable);

        foreach (DataRow row in dataTable.Rows)
        {
            T model = new T();
            foreach (var kvp in propertyMap)
            {
                kvp.Value.SetValue(model, row[kvp.Key]);
            }
            yield return model;
        }
    }

    // 辅助方法:生成模型属性与DataTable列的映射关系(示例实现)
    private static Dictionary<string, System.Reflection.PropertyInfo> GetPropertyColumnMap<T>(DataTable table)
    {
        var map = new Dictionary<string, System.Reflection.PropertyInfo>();
        foreach (var prop in typeof(T).GetProperties())
        {
            if (table.Columns.Contains(prop.Name))
            {
                map.Add(prop.Name, prop);
            }
        }
        return map;
    }
}

步骤2:添加独立的事件订阅扩展

写一个专门处理PropertyChanged订阅的扩展方法,同样基于延迟执行枚举:

public static class ModelEnumerableExtensions
{
    // 为序列中的每个模型订阅PropertyChanged事件
    public static IEnumerable<T> WithPropertyChangeSubscriptions<T>(this IEnumerable<T> models, 
        PropertyChangedEventHandler handler) 
        where T : INotifyPropertyChanged
    {
        foreach (var model in models)
        {
            model.PropertyChanged += handler;
            yield return model;
        }
    }
}

步骤3:封装成易用的ObservableCollection转换方法

提供两个重载的ToObservableCollection方法,按需选择是否启用事件订阅:

public static class DataTableExtensions
{
    // 无事件订阅的基础版本
    public static ObservableCollection<T> ToObservableCollection<T>(this DataTable dataTable) 
        where T : INotifyPropertyChanged, new()
    {
        return new ObservableCollection<T>(dataTable.ToModels<T>());
    }

    // 带事件订阅的重载版本
    public static ObservableCollection<T> ToObservableCollection<T>(this DataTable dataTable, 
        PropertyChangedEventHandler handler) 
        where T : INotifyPropertyChanged, new()
    {
        return new ObservableCollection<T>(
            dataTable.ToModels<T>().WithPropertyChangeSubscriptions(handler)
        );
    }
}

使用方式

  • 不需要订阅事件时:
var plainCollection = dataTable.ToObservableCollection<MyModel>();
  • 需要订阅事件时:
var collectionWithSubscriptions = dataTable.ToObservableCollection<MyModel>(OnModelPropertyChanged);

// 示例事件处理方法
private void OnModelPropertyChanged(object sender, PropertyChangedEventArgs e)
{
    // 处理属性变更逻辑
}

为什么不会二次迭代

ToModels和WithPropertyChangeSubscriptions都是延迟执行的枚举器,只有当ObservableCollection构造函数开始枚举序列时,才会逐行执行完整流程:创建模型→填充属性→订阅事件→添加到集合。整个过程仅遍历DataTable一次,完全避免了二次迭代的性能损耗。

内容的提问来源于stack exchange,提问作者Azzarrel

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 01:25:00