如何简化仅类内部可修改的ObservableCollection实现?
问题
我经常需要一种仅能在类内部修改引用或内容的ReadOnly ObservableCollection,因为要用于WPF绑定,所以必须基于ObservableCollection实现。当前的方案需要两个私有字段加构造器代码,要是多个这类集合的话会让代码变得臃肿,可读性下降。
现有实现示例
public class MyClass { public ReadOnlyCollection<string> Values { get { return _valuesRO; } } private readonly ObservableCollection<string> _values; private readonly ReadOnlyObservableCollection<string> _valuesRO; public MyClass() { _values = new(); _valuesRO = new(_values); } public void AddValue(string value) { _values.Add(value + ":"); } } public static class MyApp { public static void Main() { MyClass myClass = new(); myClass.AddValue("192"); //-> 应正常执行 Debug.Print(myClass.Values[0]); //-> 应正常执行 myClass.Values = new(); //-> 不应允许执行 myClass.Values.Add("192"); //-> 不应允许执行 } }
这个方案已经用了14年,现在想找更简洁的实现:不需要额外字段和构造器代码,只用一句声明初始化,比如自定义MySpecialReadOnlyCollection类,理想实现如下:
理想实现示例
public class MyClass { public MySpecialReadOnlyCollection<string> Values { get; init; } = new(); public void AddValue(string value) { Values.Add(value + ":"); } } public static class MyApp { public static void Main() { MyClass myClass = new(); myClass.AddValue("192"); //-> 应正常执行 Debug.Print(myClass.Values[0]); //-> 应正常执行 myClass.Values = new(); //-> 不应允许执行 myClass.Values.Add("192"); //-> 不应允许执行 } }
如果没法实现这种理想方案,请问次优方案是什么?核心需求是无集合后备字段和构造器代码,提升可扩展性,主要目的是明确代码意图、避免意外修改,不用考虑反射这类非常规修改的安全性。
解决方案
理想方案的可行实现
通过自定义封装类可以实现接近理想的效果,核心思路是让集合对外暴露只读接口,内部保留修改能力:
自定义MySpecialReadOnlyCollection类
public class MySpecialReadOnlyCollection<T> : ReadOnlyObservableCollection<T> { private readonly ObservableCollection<T> _innerCollection; // 内部构造器,限制仅同程序集内创建实例 internal MySpecialReadOnlyCollection() : base(new ObservableCollection<T>()) { _innerCollection = (ObservableCollection<T>)Items; } // 内部暴露修改方法,仅同程序集内可调用 internal void Add(T item) => _innerCollection.Add(item); internal void Remove(T item) => _innerCollection.Remove(item); internal void Clear() => _innerCollection.Clear(); // 按需添加其他需要的修改方法 }
使用示例
public class MyClass { public MySpecialReadOnlyCollection<string> Values { get; init; } = new(); public void AddValue(string value) { // 同程序集内可直接调用internal修改方法 Values.Add(value + ":"); } }
该实现完全满足需求:
- 外部仅能看到
ReadOnlyObservableCollection的只读成员,无法调用修改方法 - 内部无需额外字段,只用一句声明完成初始化
- 外部无法重新赋值
Values(依赖init访问器)
如果需要跨程序集使用,可将构造器和修改方法标记为protected internal,或确保业务类与自定义集合处于同一程序集。
次优方案:简化现有模式
如果不想自定义集合类,可以用C#的语法特性精简原有实现,去掉构造器冗余代码:
public class MyClass { private readonly ObservableCollection<string> _values = new(); public ReadOnlyObservableCollection<string> Values { get; } = new(_values); public void AddValue(string value) { _values.Add(value + ":"); } }
这个方案将字段初始化直接放在声明处,省去了构造器代码,虽然仍保留私有字段,但代码简洁度和可读性大幅提升,是原有模式的最优精简版本。
内容的提问来源于stack exchange,提问作者Justin8051
相关产品推荐
相关产品推荐

