如何在不二次迭代的情况下拆分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
相关产品推荐
相关产品推荐

