实现IEnumerable 和 IEnumerator, 使类型具备枚举功能的注意事项

39 篇文章 0 订阅
9 篇文章 0 订阅

实现IEnumerable 和 IEnumerator, 使类型具备枚举功能的注意事项

背景知识

在现实应用中,对于集合以及枚举这些集合的需求非常普遍, 因此在.NET中集合所依赖的接口被设计为公共的。想要实现对象的枚举就必须继承IEnumerable接口。
public interface IEnumerable
{
      public IEnumerator GetEnumerator();
}

它只有一个成员:GetEnumerator方法。该方法返回一个世纪的枚举器(enumerator)对象。那么要定义一个枚举器就要必须实现IEnumrator接口:
public interface IEnumerator
{
      public Boolean MoveNext();
      public Object Current { get; }
      public void Reset();
}

这里我不想把MSDN上的解释在重复一遍,我只是提出一个问题,也就是说如果我们希望一个类型支持枚举特性,那么我们就必须还要额外定义一个辅助类型来实现IEnumerator的所有方法。这个辅助类型通常被定义为一个Nested class声明在主类内部。这样在实现的时候我们发现,在app调用GetEnumerator()方法时,我们需要构造一个辅助类型的实例作为返回值。这时我们有两种做法:

  1. 把主类型的数据当时的静态快照,以复本的形式在辅助类型初始化时赋值给辅助类型实例(enumerator)
  2. 把主类型的数据的引用传递给辅助类型,保证app访问的实时更新的数据

这的确提供了一定的灵活性给用户, 在app想要枚举某个实例的数据集合时,也可以有两种方法:

  1. 使用各接口暴露的方法
  2. 使用foreach语句:foreach( type-identifier in expression ) { embedded-statement }

对于foreach,我想提两句,要想使用它所必须满足的条件:

  1. expression必须实现GetEnumerator()方法
  2. GetEnumerator()放回的实例必须实现MoveNext()和Current两个公共方法。

问题1:为什么需要两个接口,而不把Current, MoveNext和Reset成员都放到IEnumerable接口中?

答案:采用这种间接的方式是为了提高灵活性。你可以利用IEnumerator来决定如何展示你想要暴露的内部数据给客户。当然你可以在一个类型中同时实现这两个接口:class MyArrayData : IEnumerable, IEnumerator { ... };这样可以节省了一些创建辅助类型从而带来的CPU和Memory开销。一般建议在如下情况时,使用这种结合的方法:

  1. 当数据没有存储在系统已有的集合中,否则可以直接把集合的enumerator返回
  2. 当你的自定义的Enumerator需要做出了移动游标和返回数据之外的其他操作

问题2:如果集合中的数据是值类型,那么有什么性能上的考虑么?

答案:由于值类型在传入ArrayList这样的集合容器中时,需要box,在传出的时候需要unbox。经过很多验证,这种操作时非常耗时的。因此,我们可以在实现标准的枚举方法的时候,可以在暴露一些自定义的GetEnumerator, MoveNext和Current方法(其实, 标准的接口只是调用自定义的接口而已的一个wrapper方法)。这样既可以在自己的方法中避免了不必要的装箱和拆箱操作(用自定义方法,而不能使用foreach语句),又可以被约定俗成的foreach句法所调用(当然,这种情况下就不能避免box和unbox了)。
 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值