找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 6978|回复: 100

VB.NET实现IEnumerator接口实例教程

[复制链接]
发表于 2017-1-20 15:32:06 | 显示全部楼层 |阅读模式

 VB.NET经过长时间的发展,很多用户都很了解VB.NET实现IEnumerator接口了,这里我发表一下个人理解,和大家讨论讨论。在面向对象的设计中,经常会用到有类似父子关系的这个对象,比如在我现在的一个项目中,有订单对象,在一个订单下又包含多个产品,这时我就想用 Iterator模式来封装订单下的产品,在dot Net中的IEnumerator接口就是用来实现迭代的,来支持dot Net中的for each的操作。

  要VB.NET实现IEnumerator接口,需在实现以下几个函数来支持IEnumerator接口的操作

  Overridable ReadOnly Property Current() As Object

  Current用于在迭代过程中得到当前的对象

  Public Overridable Function MoveNext() As Boolean

  MoveNext用于在迭代过程中将迭代指针指向下一个对象,初始是迭代指针指向集合的开始(在第一个节点之前的位置),一旦越过集合的结尾,在调用 Reset 之前,对 MoveNext 的后续调用返回 false。

  Overridable Sub Reset()

  将枚举数设置为其初始位置,该位置位于集合中第一个元素之前。

  只要集合保持不变,枚举数就将保持有效。如果对集合进行了更改(例如添加、修改或删除元素),则该枚举数将失效且不可恢复,并且下一次对 MoveNext 或 Reset 的调用将引发InvalidOperationException。

  下需是一个具体的VB.NET实现IEnumerator接口的对像


  Imports System.Collections
  
  '在此实际实现的是System.Collections.IEnumerable接口,
  IteratorProduct 用此接口来向使用者提供对IEnumerator接口的操作。
  
  Public Class IteratorProduct : Implements System.Collections.IEnumerable
  Private Products As Collection '用Collection在存订单中的所有产品
  Private item As Integer = -1
  
  Public Sub New()
  Products = New Collection
  Products.Add("xh") '这只是为了测试方便,将加入产品的内容直接写在这了
  Products.Add("lj")
  Products.Add("qd")
  End Sub
  
  Overridable ReadOnly Property Current() As Object
  Get
  Return Products(item)
  End Get
  End Property
  
  Public Overridable Function MoveNext() As Boolean
  item += 1
  End Function
  
  Overridable Sub Reset()
  item = -1
  End Sub
  
  '返回迭代对像给使用者
  
  Overridable Function GetEnumerator()
  As IEnumerator Implements IEnumerable.GetEnumerator
  Return Me.Products.GetEnumerator
  End Function
  
  End Class


qq.jpg
 楼主| 发表于 2017-1-20 15:35:58 | 显示全部楼层

在C#中凡是实现了IEnumerator接口的数据类型都可以用foreach语句进行迭代访问.可是自定义类型如何实现IEnumerator接口来支持foreach迭代呢?

要实现这个功能,先来看看IEnumerable和IEnumerator接口的定义.

public interface IEnumerable

{

          //IEnumerable只有一个方法,返回可循环访问集合的枚举数.

          IEnumerator GetEnumerator();

}

public interface IEnumerator

{

         //移动到集合的下一个元素,如果成功返回true,如果失败返回false

         bool MoveNext();

         //将集合重置为初始位置,这个位置在集合第一个元素之前(类似于-1的状态)

        void Reset();

        //属性,访问集合的当前元素

       object Current{get;}

}

IEnumerator是所有枚举数的基接口.枚举数只允许读取集合中的数据(是只读的,在遍历过程中不可进行修改).枚举数无法用于修改基础集合.最初,枚举数被定位于集合第一个元素的前面.Reset()也将枚举数设置到此位置.在此位置调用Current会引发异常.因此在调用Current之前,必须先调用MoveNext(),将枚举数提前到集合第一个元素,在调用MoveNext和Reset这前,Current会返回同一个对象,MoveNext将Current设置为下一个元素,当使用MoveNext到集合的末尾时,枚举数会被设置到集合最后一个元素的后面.,这时调用MoveNext会返回false.如果调用MoveNext返回false,此时再调用Current会引发一个异常.如果要再次让Current访问集合的第一个元素,应先调用Reset然后再调用MoveNext;

注:实现上是把位于集合首部和尾部的特殊判断转移到了MoveNext函数中.这样我们在使用IEnumerator这个接口会,具体的实现会很漂亮,省事.

枚举数没有对集合的独占访问权,因此枚举一个集合在本质上不是一个线程安全的过程.在枚举一个集合的过程中,其它线程仍可以修改该集合.这会导致枚举数引发异常.如果要在枚举过程中保证线程安全,可以在整个枚举过程事锁定集合,或者捕捉由于其他纯种进行的更改而引发的异常.

下面来总结一下IEnumerable和IEnumerator的区别

1.一个集合要想通过foreach进行迭代就必须实现IEnumearble接口(必须以某种方式返回一个IEnumerator对象)

2.IEnumerator对象具体来实现了一个迭代器

3.从这两个接口的用词选择上可以看出:IEnumerable是一个声明式接口,声明实现该接口的类是可以枚举的,但没有说明如何实现枚举器.

  IEnumerator是一个实现式接口,IEnumerator对象就是一个迭代器.

4.IEnumerable和IEnumerator接口通过IEnumerable接口的GetEnumerator方法建立连接,客户可以通过IEnumerable接口的GetEnumerator方法来得到一个IEnumerator对象

  在这个意思上可以将GetEnumerator方法看成是IEnumerator的工厂方法.

[csharp] view plain copy


  • public class Person  
  •   {  
  •       public string Name { get; set; }  
  •       public string Sex { get; set; }  
  •   }  
  •   //枚举器的实现  
  •   class PersonEnumerator:IEnumerator<Person>  
  •   {  
  •       int _currentIndex;  
  •       Person[] _psersons;  
  •   
  •       public PersonEnumerator(Person[] _psersons)  
  •       {  
  •           this._psersons = _psersons;  
  •           _currentIndex = -1;  
  •       }  
  •       public Person Current  
  •       {  
  •           get  
  •           {  
  •               if (_currentIndex < 0 || _currentIndex > _psersons.Length)  
  •               {  
  •                   throw new InvalidOperationException("索引越界");  
  •               }  
  •               return _psersons[_currentIndex];  
  •           }  
  •       }  
  •   
  •       object System.Collections.IEnumerator.Current  
  •       {  
  •           get  
  •           {  
  •               if (_currentIndex < 0 || _currentIndex > _psersons.Length)  
  •               {  
  •                   throw new InvalidOperationException("索引越界");  
  •               }  
  •               return _psersons[_currentIndex];  
  •           }  
  •       }  
  •   
  •       public bool MoveNext()  
  •       {  
  •           _currentIndex++;  
  •           return _currentIndex < _psersons.Length;  
  •       }  
  •   
  •       public void Reset()  
  •       {  
  •           _currentIndex = -1;  
  •       }  
  •   
  •       public void Dispose()  
  •       {  
  •             
  •       }  
  •   }  
  •   public class Persons : IEnumerable<Person>  
  •   {  
  •       Person[] _persons;  
  •       public Persons()  
  •       {  
  •           _persons = new Person[]  
  •           {  
  •               new Person  
  •               {  
  •                    Name = "1",  
  •                    Sex="---",  
  •   
  •               },  
  •               new Person  
  •               {  
  •                    Name = "2",  
  •                    Sex="---",  
  •   
  •               },  
  •           };  
  •       }  
  •       public IEnumerator<Person> GetEnumerator()  
  •       {  
  •           return new PersonEnumerator(_persons);  
  •       }  
  •   
  •       System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()  
  •       {  
  •           return new PersonEnumerator(_persons);  
  •       }  
  •   }  


 楼主| 发表于 2017-1-20 15:32:35 | 显示全部楼层
在C#中凡是实现了IEnumerator接口的数据类型都可以用foreach语句进行迭代访问.可是自定义类型如何实现IEnumerator接口来支持foreach迭代呢?
要实现这个功能,先来看看IEnumerable和IEnumerator接口的定义.
public interface IEnumerable
{
          //IEnumerable只有一个方法,返回可循环访问集合的枚举数.
          IEnumerator GetEnumerator();
}
public interface IEnumerator
{
         //移动到集合的下一个元素,如果成功返回true,如果失败返回false
         bool MoveNext();
         //将集合重置为初始位置,这个位置在集合第一个元素之前(类似于-1的状态)
        void Reset();
        //属性,访问集合的当前元素
       object Current{get;}
}
IEnumerator是所有枚举数的基接口.枚举数只允许读取集合中的数据(是只读的,在遍历过程中不可进行修改).枚举数无法用于修改基础集合.最初,枚举数被定位于集合第一个元素的前面.Reset()也将枚举数设置到此位置.在此位置调用Current会引发异常.因此在调用Current之前,必须先调用MoveNext(),将枚举数提前到集合第一个元素,在调用MoveNext和Reset这前,Current会返回同一个对象,MoveNext将Current设置为下一个元素,当使用MoveNext到集合的末尾时,枚举数会被设置到集合最后一个元素的后面.,这时调用MoveNext会返回false.如果调用MoveNext返回false,此时再调用Current会引发一个异常.如果要再次让Current访问集合的第一个元素,应先调用Reset然后再调用MoveNext;
注:实现上是把位于集合首部和尾部的特殊判断转移到了MoveNext函数中.这样我们在使用IEnumerator这个接口会,具体的实现会很漂亮,省事.
枚举数没有对集合的独占访问权,因此枚举一个集合在本质上不是一个线程安全的过程.在枚举一个集合的过程中,其它线程仍可以修改该集合.这会导致枚举数引发异常.如果要在枚举过程中保证线程安全,可以在整个枚举过程事锁定集合,或者捕捉由于其他纯种进行的更改而引发的异常.
下面来总结一下IEnumerable和IEnumerator的区别
1.一个集合要想通过foreach进行迭代就必须实现IEnumearble接口(必须以某种方式返回一个IEnumerator对象)
2.IEnumerator对象具体来实现了一个迭代器
3.从这两个接口的用词选择上可以看出:IEnumerable是一个声明式接口,声明实现该接口的类是可以枚举的,但没有说明如何实现枚举器.
  IEnumerator是一个实现式接口,IEnumerator对象就是一个迭代器.
4.IEnumerable和IEnumerator接口通过IEnumerable接口的GetEnumerator方法建立连接,客户可以通过IEnumerable接口的GetEnumerator方法来得到一个IEnumerator对象
  在这个意思上可以将GetEnumerator方法看成是IEnumerator的工厂方法.
[csharp] view plain copy


  • public class Person
  •   {
  •       public string Name { get; set; }
  •       public string Sex { get; set; }
  •   }
  •   //枚举器的实现
  •   class PersonEnumerator:IEnumerator<Person>
  •   {
  •       int _currentIndex;
  •       Person[] _psersons;
  •       public PersonEnumerator(Person[] _psersons)
  •       {
  •           this._psersons = _psersons;
  •           _currentIndex = -1;
  •       }
  •       public Person Current
  •       {
  •           get
  •           {
  •               if (_currentIndex < 0 || _currentIndex > _psersons.Length)
  •               {
  •                   throw new InvalidOperationException("索引越界");
  •               }
  •               return _psersons[_currentIndex];
  •           }
  •       }
  •       object System.Collections.IEnumerator.Current
  •       {
  •           get
  •           {
  •               if (_currentIndex < 0 || _currentIndex > _psersons.Length)
  •               {
  •                   throw new InvalidOperationException("索引越界");
  •               }
  •               return _psersons[_currentIndex];
  •           }
  •       }
  •       public bool MoveNext()
  •       {
  •           _currentIndex++;
  •           return _currentIndex < _psersons.Length;
  •       }
  •       public void Reset()
  •       {
  •           _currentIndex = -1;
  •       }
  •       public void Dispose()
  •       {
  •       }
  •   }
  •   public class Persons : IEnumerable<Person>
  •   {
  •       Person[] _persons;
  •       public Persons()
  •       {
  •           _persons = new Person[]
  •           {
  •               new Person
  •               {
  •                    Name = "1",
  •                    Sex="---",
  •               },
  •               new Person
  •               {
  •                    Name = "2",
  •                    Sex="---",
  •               },
  •           };
  •       }
  •       public IEnumerator<Person> GetEnumerator()
  •       {
  •           return new PersonEnumerator(_persons);
  •       }
  •       System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
  •       {
  •           return new PersonEnumerator(_persons);
  •       }
  •   }


您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|

GMT+8, 2026-10-31 13:02

安卓脚本软件 · 键鼠盒子 · HID · 机械臂

快速回复 返回顶部 返回列表

随机推荐 · 热帖 / 新帖 / 随便看看

社区会员(随机展示)