如何在数组类型属性的元素发生变更时调用指定方法
问题根因
这个现象是C#的类型系统和属性机制的正常表现:
- 数组是引用类型,执行
classInstance.MyArray[3] = 5时,首先通过属性的get访问器拿到内部数组的引用,后续的元素赋值是直接修改堆上的数组实例内存,全程不会调用属性的set访问器,自然不会触发你写在set里的CheckInput逻辑。 - 原生
double[]没有内置元素修改的监听钩子,你无法直接在普通数组实例上捕获单元素修改动作。
可行解决方案
方案1:封装自定义校验数组包装类(最优)
不要直接暴露原生数组,自己实现一个带校验逻辑的数组包装类型,所有元素修改都走类型自带的索引器,从入口处捕获所有修改动作:
public class CheckedDoubleArray { private readonly double[] _storage; public CheckedDoubleArray(int length) { _storage = new double[length]; } public double this[int index] { get => _storage[index]; set { _storage[index] = value; // 单元素修改后直接触发校验逻辑 ValidateArray(_storage); } } public int Length => _storage.Length; // 可选:批量修改时使用,避免多次重复触发校验 public void BatchModify(Action<double[]> modifyAction) { modifyAction(_storage); ValidateArray(_storage); } private void ValidateArray(double[] array) { // 把原有的CheckInput逻辑迁移到此处即可 if (array.Any(d => double.IsNaN(d) || double.IsInfinity(d))) throw new ArgumentException("数组包含非法数值"); } }
对应属性可以简化为自动属性实现:
public CheckedDoubleArray MyArray { get; } = new CheckedDoubleArray(10);
这个方案性能和原生数组几乎无差异,不存在校验逻辑被绕过的可能,是最稳妥的实现。
方案2:保留原生数组接口,禁止直接修改返回值
如果你必须对外暴露double[]类型,就不要在get访问器返回内部数组的真实引用,改为返回数组的拷贝,同时提供专门的元素修改入口:
private double[] _myArray = new double[10]; public double[] MyArray { // 返回深拷贝,外部对返回数组的修改不会影响内部存储 get => (double[])_myArray.Clone(); set { CheckInput(value); _myArray = value; } } public void SetMyArrayValue(int index, double value) { _myArray[index] = value; CheckInput(_myArray); }
这个方案的缺点是调用方无法使用obj.MyArray[i] = x的直观写法,且大数组每次get都做拷贝会产生额外性能开销。
避坑提醒
- 不推荐用
ObservableCollection<double>替代数组实现这个需求:虽然它的元素修改会触发CollectionChanged事件,但你把集合实例暴露出去后,外部代码可以随意注销你注册的校验事件,存在校验被绕过的风险。 - 不要使用内存监听、IL注入之类的黑科技捕获数组修改,稳定性极差且性能损耗极高,不适合生产环境。
内容的提问来源于stack exchange,提问作者Amos Egel
相关产品推荐
相关产品推荐

