C#类构造函数接收集合参数:如何避免不必要的列表拷贝?
类存储集合参数的选型与避免不必要拷贝的方案
一、集合参数类型的选择
当类需要存储传入的集合参数时,优先根据场景选择以下类型:
IReadOnlyList<T>/IReadOnlyCollection<T>:作为参数类型时,明确告知调用方该集合仅用于读取,无需修改,自身存储时也能保证对外暴露的不可变性。IEnumerable<T>:如果需要兼容更广泛的集合来源(比如LINQ查询结果、自定义迭代器等),可以用它作为参数,但内部需要做额外处理来避免性能损耗和确保不可变性。
二、避免IEnumerable<T>处理时的多余拷贝
如果用IEnumerable<T>作为参数,直接调用items.ToList().AsReadOnly()会在很多场景下产生不必要的拷贝(比如传入的已经是List<T>或者只读集合)。可以通过类型判断+构造函数重载的方式优化:
优化后的事件参数类示例
public class CustomEventArgs : EventArgs { public IReadOnlyList<ItemType> Items { get; } // 兼容最广泛的IEnumerable<T>输入 public CustomEventArgs(IEnumerable<ItemType> items) { Items = items switch { IReadOnlyList<ItemType> readOnly => readOnly, List<ItemType> list => list.AsReadOnly(), _ => items.ToList().AsReadOnly() }; } // 直接接受List<T>,跳过类型检查,直接包装为只读 public CustomEventArgs(List<ItemType> items) { Items = items.AsReadOnly(); } // 直接接受只读集合,直接复用 public CustomEventArgs(IReadOnlyList<ItemType> items) { Items = items; } }
优化逻辑说明
- 当调用
new CustomEventArgs(new List<ItemType> { item1, item2 })时,会匹配List<T>重载,直接调用AsReadOnly()生成只读包装,无集合拷贝。 - 当传入内部维护的
_items(比如List<T>类型),同样通过重载直接包装,避免拷贝。 - 仅当传入的是只能单次枚举的
IEnumerable<T>(比如LINQ查询结果、自定义迭代器)时,才会执行ToList()拷贝为列表,再转只读。
额外注意事项
如果需要存储的是集合的快照(即原集合后续修改不影响存储的内容),那么无论传入什么类型,都必须执行拷贝。但如果事件发布者能保证原集合在发布后不会被修改,用AsReadOnly()包装即可,既保证不可变性又避免性能损耗。
内容的提问来源于stack exchange,提问作者gartenriese
相关产品推荐
相关产品推荐

