23.自定义容器支持foreach循环
23.1 题目
C#中如何让自定义容器类能够使用foreach循环遍历?
23.2 深入解析
通过为自定义容器类实现迭代器,可以使其能够使用foreach循环遍历。下面介绍两种实现方式:
传统方式
实现 IEnumerable / IEnumerable<T> 与 IEnumerator / IEnumerator<T>(或仅用 yield 实现 GetEnumerator),并提供相应成员。
步骤:
- 实现
IEnumerable接口的GetEnumerator方法。 - 实现
IEnumerator接口的Current属性和MoveNext方法。
代码示例:
using System;
using System.Collections;
using System.Collections.Generic;
public class MyContainer<T> : IEnumerable<T>
{
private T[] items;
private int count;
public MyContainer(int capacity)
{
items = new T[capacity];
count = 0;
}
public void Add(T item)
{
if (count < items.Length)
{
items[count++] = item;
}
}
public IEnumerator<T> GetEnumerator()
{
return new MyEnumerator(this);
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
private class MyEnumerator : IEnumerator<T>
{
private MyContainer<T> container;
private int index;
public MyEnumerator(MyContainer<T> container)
{
this.container = container;
index = -1;
}
public T Current
{
get
{
if (index >= 0 && index < container.count)
{
return container.items[index];
}
throw new InvalidOperationException();
}
}
object IEnumerator.Current => Current;
public bool MoveNext()
{
return ++index < container.count;
}
public void Reset()
{
index = -1;
}
public void Dispose() { }
}
}
语法糖方式
利用yield return语法糖,只需实现GetEnumerator方法即可完成迭代器的实现。
代码示例:
using System;
using System.Collections.Generic;
public class MyContainer<T> : IEnumerable<T>
{
private T[] items;
private int count;
public MyContainer(int capacity)
{
items = new T[capacity];
count = 0;
}
public void Add(T item)
{
if (count < items.Length)
{
items[count++] = item;
}
}
public IEnumerator<T> GetEnumerator()
{
for (int i = 0; i < count; i++)
{
yield return items[i];
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
总结
- 传统方式:手动实现
IEnumerator<T>与IEnumerable<T>的成员。适合需要更多控制的复杂场景。 - 语法糖方式:利用
yield return语法糖,简化了迭代器的实现,适合大多数场景。
通过上述两种方式,可以让自定义容器类支持foreach循环遍历,使代码更加简洁和易于维护。
23.3 答题示例
“在C#中让自定义容器支持
foreach循环遍历需实现迭代器(Iterator),核心是通过实现IEnumerable<T>接口并提供GetEnumerator方法。具体实现方式有两种:
- 传统接口实现:
- 容器类需继承
IEnumerable<T>接口,并实现GetEnumerator方法返回一个IEnumerator<T>对象。- 枚举器类需实现
IEnumerator<T>接口的MoveNext(移动游标)、Current(获取当前元素)和Reset(重置游标)方法。- 示例代码:
public class MyContainer<T> : IEnumerable<T> { private T[] items; public IEnumerator<T> GetEnumerator() { return new MyEnumerator(this); } private class MyEnumerator : IEnumerator<T> { private MyContainer<T> container; private int index = -1; public T Current => container.items[index]; object IEnumerator.Current => Current; public bool MoveNext() => ++index < container.items.Length; public void Reset() => index = -1; public void Dispose() { } } }语法糖方式(推荐):
- 利用
yield return关键字简化迭代器实现,只需在GetEnumerator方法中使用循环逐个返回元素。- 示例代码:
public class MyContainer<T> : IEnumerable<T> { private T[] items; public IEnumerator<T> GetEnumerator() { for (int i = 0; i < items.Length; i++) { yield return items[i]; // 自动生成迭代器逻辑 } } }- 使用场景:
- 传统方式:适合需要自定义迭代逻辑(如延迟加载、复杂过滤)的场景。
- 语法糖方式:适用于简单线性遍历,代码简洁且不易出错。
最终,通过实现IEnumerable<T>接口,容器类即可直接使用foreach循环遍历元素。”
23.4 关键词联想
- IEnumerable
接口 - IEnumerator
枚举器 - yield return 语法糖
MoveNext() 方法- Current 属性
迭代器模式(Iterator Pattern)泛型集合(Generic Collection)延迟加载(Lazy Loading)- 枚举器状态管理
Dispose() 方法
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com