为何List<T>属性的编辑方法仅调用get访问器?如何解决?
为什么Add/Remove不触发set访问器?
List<T>是引用类型,调用属性的get访问器时,返回的是该List对象的引用。Add()、Remove()这类方法只是通过这个引用修改List内部的元素集合,并没有改变属性本身指向的引用(也就是没有给属性赋值新的List实例)。而set访问器只有在给属性赋新值时才会触发,比如MyList = new List<T>()这种场景。
解决方案
结合你的接口约束,提供几种可行方案:
方案1:自定义集合类封装同步逻辑
如果接口允许将属性类型改为IList<T>(而非硬要求List<T>),可以自定义实现IList<T>的集合类,在所有修改集合的方法中加入数据库同步逻辑:
public class DbSyncList<T> : IList<T> { private readonly List<T> _innerList = new List<T>(); public void Add(T item) { _innerList.Add(item); // 执行数据库插入逻辑 SyncToDatabase(); } public bool Remove(T item) { var removed = _innerList.Remove(item); if (removed) { // 执行数据库删除逻辑 SyncToDatabase(); } return removed; } // 实现IList<T>的其他成员(如Indexer、Insert、Clear等),按需加入同步逻辑 // ... private void SyncToDatabase() { // 你的数据库更新代码 } }
在你的实现类中返回这个自定义集合,其他不需要同步的实现类直接返回普通List<T>即可(因为List<T>也实现了IList<T>):
public class YourClass : IYourInterface { public IList<T> YourList { get; set; } = new DbSyncList<T>(); }
方案2:用ObservableCollection订阅变更事件
如果接口可改为IList<T>,可以使用ObservableCollection<T>,它会在集合元素变更时触发CollectionChanged事件,你只需订阅该事件并同步数据库:
public class YourClass : IYourInterface { private ObservableCollection<T> _yourList; public IList<T> YourList { get => _yourList; set { if (_yourList != null) { _yourList.CollectionChanged -= OnListChanged; } _yourList = value as ObservableCollection<T> ?? new ObservableCollection<T>(value); _yourList.CollectionChanged += OnListChanged; // 赋值新集合时可同步全量数据 SyncToDatabase(); } } private void OnListChanged(object sender, NotifyCollectionChangedEventArgs e) { // 根据变更类型(添加、删除等)执行对应数据库操作,也可通过e.NewItems/e.OldItems做精细处理 SyncToDatabase(); } private void SyncToDatabase() { // 你的数据库更新逻辑 } }
其他实现类依旧返回List<T>,赋值时会自动被包装为ObservableCollection<T>。
方案3:封装操作方法,限制直接修改集合
如果接口必须保持List<T>类型且无法修改,可将属性设为只读,在类中提供专门的添加/删除方法,在方法内操作集合并同步数据库:
public class YourClass : IYourInterface { private readonly List<T> _yourList = new List<T>(); // 返回只读包装,阻止外部直接调用修改方法 public List<T> YourList => _yourList.AsReadOnly() as List<T>; public void AddItem(T item) { _yourList.Add(item); SyncToDatabase(); } public void RemoveItem(T item) { if (_yourList.Remove(item)) { SyncToDatabase(); } } private void SyncToDatabase() { // 数据库更新逻辑 } }
注:AsReadOnly()的强制转换虽不严谨,但能避免大部分非恶意的直接修改;若接口允许,改为返回IReadOnlyList<T>会更规范。
接口兼容建议
如果接口硬要求List<T>,方案3是最直接的选择;若可将接口属性类型改为IList<T>,方案1或2会更优雅,既能满足同步需求,也不影响其他实现类使用普通List<T>。
内容的提问来源于stack exchange,提问作者just-a-hriday

