如何避免IReadOnlyCollection<T>含默认项?有无禁止默认值的不可变集合?
问题解答
核心疑问
是否存在禁止用默认值初始化的不可变集合,以此避免因集合中包含default(MyObject)(引用类型即null),导致后续调用Clone方法时程序崩溃的问题?
场景复现
当前代码中,用默认值初始化数组的写法:
var newObjects = new MyObject[] { default };
MyObject的Clone方法在对象为默认值(null)时会抛出异常,因为直接访问了未初始化的属性:
public MyObject Clone() { return new MyObject(ShortName, Name); }
调用CopyObjects遍历集合时,会因null对象调用Clone触发崩溃:
public IEnumerable<MyObject> CopyObjects(IReadOnlyCollection<MyObject> newObjects) { var newLst = new List<MyObject>(); foreach (var o in newObjects) { newLst.Add(o.Clone()); } return newLst; }
解决方案分析
1. 原生集合无此限制
.NET原生的不可变集合(如ImmutableList<T>、ImmutableArray<T>)本身不会阻止添加default(T),需要从数据校验逻辑层面解决问题,而非依赖集合本身的特性。
2. 可行解决思路
- 自定义带校验的不可变集合:封装原生不可变集合,在添加元素时校验是否为默认值,违规则抛出异常。示例:
public sealed class NonDefaultImmutableList<T> { private readonly ImmutableList<T> _innerList; private NonDefaultImmutableList(ImmutableList<T> innerList) { _innerList = innerList; } public static NonDefaultImmutableList<T> Empty => new NonDefaultImmutableList<T>(ImmutableList<T>.Empty); public NonDefaultImmutableList<T> Add(T item) { if (EqualityComparer<T>.Default.Equals(item, default(T))) { throw new ArgumentException("禁止添加默认值到集合中", nameof(item)); } return new NonDefaultImmutableList<T>(_innerList.Add(item)); } // 按需实现AddRange、Remove等其他不可变集合方法,均添加校验逻辑 public IEnumerator<T> GetEnumerator() => _innerList.GetEnumerator(); }
使用时,添加default(MyObject)会直接触发报错,从源头避免后续崩溃。
- 在业务方法中添加校验:在
CopyObjects遍历克隆前,先过滤或校验null对象:
public IEnumerable<MyObject> CopyObjects(IReadOnlyCollection<MyObject> newObjects) { var newLst = new List<MyObject>(); foreach (var o in newObjects) { if (o == null) { // 可选:抛出异常、跳过或记录日志 throw new InvalidOperationException("Null对象无法执行克隆操作"); // 或者跳过该元素:continue; } newLst.Add(o.Clone()); } return newLst; }
- 优化实体类的容错性:在
Clone方法内部处理默认值情况:
public MyObject Clone() { if (this == null) { // 可返回null或一个空实例,根据业务需求选择 return null; // return new MyObject(string.Empty, string.Empty); } return new MyObject(ShortName, Name); }
总结
.NET没有原生的不可变集合会自动禁止默认值初始化,但可以通过自定义带校验的不可变集合、在业务方法中加入校验逻辑或优化实体类方法的容错性,从源头或中间环节避免因默认值导致的程序崩溃。
内容的提问来源于stack exchange,提问作者Steffen Skaraas
相关产品推荐
相关产品推荐

