如何为ReadOnlyCollection<T>实现按名称查找的自定义字符串索引器
解决方案
要实现按Animal.Name的字符串索引器,同时保持集合的只读特性且不使用字典,你可以自定义一个只读集合类,替代系统自带的ReadOnlyCollection<Animal>。系统的ReadOnlyCollection是密封类无法继承扩展,所以我们自己实现一个符合只读要求的集合,并添加自定义索引器。
步骤1:自定义只读集合类
创建一个实现IReadOnlyList<Animal>的类,内部包装你的私有List<Animal>,同时添加字符串索引器:
public class ReadOnlyAnimalCollection : IReadOnlyList<Animal> { private readonly List<Animal> _innerList; // 构造函数,传入内部要包装的List public ReadOnlyAnimalCollection(List<Animal> innerList) { _innerList = innerList ?? throw new ArgumentNullException(nameof(innerList)); } // 实现IReadOnlyList的int索引器 public Animal this[int index] => _innerList[index]; // 添加自定义字符串索引器,按Name查找 public Animal this[string name] { get { var animal = _innerList.FirstOrDefault(a => a.Name == name); if (animal == null) throw new KeyNotFoundException($"找不到名为{name}的动物"); return animal; } } // 实现IReadOnlyCollection的Count属性 public int Count => _innerList.Count; // 实现IEnumerable的枚举器 public IEnumerator<Animal> GetEnumerator() { return _innerList.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } }
步骤2:修改Zoo类的Animals属性
把原来的ReadOnlyCollection<Animal>替换成自定义的ReadOnlyAnimalCollection:
public class Animal { public string Name { get; set; } } public class Zoo { private List<Animal> _animals = new List<Animal>(); // 对外暴露自定义的只读集合 public ReadOnlyAnimalCollection Animals => new ReadOnlyAnimalCollection(_animals); public void AddAnimal(Animal animal) { if (animal == null) throw new ArgumentNullException(nameof(animal)); _animals.Add(animal); } }
步骤3:使用示例
现在你可以同时通过int索引和字符串索引访问集合:
Zoo z = new Zoo(); z.AddAnimal(new Animal() { Name = "Lion" }); z.AddAnimal(new Animal() { Name = "Tiger" }); // 通过int索引访问 var lionByIndex = z.Animals[0]; // 通过Name字符串索引访问 var tigerByName = z.Animals["Tiger"];
关键说明
- 自定义集合完全基于原有的
List<Animal>,不需要额外的字典存储,符合你的要求; - 集合对外只暴露只读接口(
IReadOnlyList),确保外部无法修改集合内容,所有修改必须通过Zoo的AddAnimal方法; - 字符串索引器内部使用
FirstOrDefault遍历查找,若需要忽略大小写,可以修改为a.Name.Equals(name, StringComparison.OrdinalIgnoreCase); - 如果不想在找不到时抛出异常,也可以改为返回
null,只需去掉throw语句直接返回animal即可。
内容的提问来源于stack exchange,提问作者Andrei Popescu
相关产品推荐
相关产品推荐

