C#如何实现同时支持字段与属性的更新检测通用辅助函数
解决方案
C# 中属性本质是get/set方法包裹的语法糖,不是可直接寻址的存储位置,因此无法作为ref参数传入,你可以通过重载+委托的方案实现两种场景的兼容,不需要复杂的表达式树解析:
1. 基础实现(无额外性能开销)
提供两个编译期匹配的重载,分别覆盖变量/字段场景、属性/嵌套属性场景:
// 重载1:适用于变量、字段(支持ref的场景),和你原有实现完全兼容,无额外开销 bool UpdateHelper<T>(ref T original, T newValue) { if (Equals(original, newValue)) return false; original = newValue; return true; } // 重载2:适用于属性、多层嵌套属性场景 bool UpdateHelper<T>(Func<T> getValue, Action<T> setValue, T newValue) { var originalValue = getValue(); if (Equals(originalValue, newValue)) return false; setValue(newValue); return true; }
2. 调用示例
变量/字段场景(原有逻辑无需修改)
bool changes = false; changes |= UpdateHelper(ref name, frmName.Text); changes |= UpdateHelper(ref description, frmDescription.Text);
属性/嵌套属性场景
// 普通属性 changes |= UpdateHelper(() => myModel.Name, v => myModel.Name = v, frmName.Text); changes |= UpdateHelper(() => myModel.Description, v => myModel.Description = v, frmDescription.Text); // 多层嵌套属性也原生支持,不需要额外适配 changes |= UpdateHelper(() => myModel.ChildObject.Name, v => myModel.ChildObject.Name = v, frmChildName.Text);
可选:表达式树封装(调用更简洁)
如果觉得委托版本写两个lambda比较繁琐,也可以封装表达式树版本的重载,自动解析生成读写委托,调用时只需传入属性表达式即可:
using System.Linq.Expressions; bool UpdateHelper<T>(Expression<Func<T>> propertyExpr, T newValue) { // 读取当前值 var getValue = propertyExpr.Compile(); var originalValue = getValue(); if (Equals(originalValue, newValue)) return false; // 生成赋值委托 var memberAccess = (MemberExpression)propertyExpr.Body; var valueParam = Expression.Parameter(typeof(T), "newVal"); var assignAction = Expression.Lambda<Action<T>>( Expression.Assign(memberAccess, valueParam), valueParam ).Compile(); assignAction(newValue); return true; }
调用方式和你预想的完全一致:
changes |= UpdateHelper(() => myModel.Name, frmName.Text); changes |= UpdateHelper(() => myModel.ChildObject.Name, frmChildName.Text);
注意:该版本每次调用会编译表达式,高频使用场景建议添加委托缓存避免重复编译的性能损耗。
内容的提问来源于stack exchange,提问作者Alain
相关产品推荐
相关产品推荐

