自定义类型使用foreach循环

原创 2016年08月30日 16:03:47

示例代码:

List<string> names = new List<string>();
foreach(string name in names)
{
    Console.WriteLine(name);
}

以上foreach语句代码中,names类型是List。c sharp允许用户自定义自己的类型以便使用foreach语句。假设有类型People定义如下,

using System;
public class People
{
    private Person[] persons;
    public People(Person[] persons)
    {
        this.persons = new Person[persons.Length];
        for(int i = 0; i < persons.Length; i++)
        {
            this.persons[i] = persons[i];
        }
    }
}

其中,Person类的定义如下:

using System;
public class Person
{
    private string name;
    private int age;

    public Person(string name, int age)
    {
        this.name = name;
        this.age = age;
    }

    public override string ToString()
    {
        return this.name + " " + this.age;
    }
}

我们期待的使用foreach的效果如下:

// persons' type is People
foreach(Person person in persons)
{
    Console.WriteLine(person);
}

foreach语句的原理是从实现IEnumerable接口的类中调用GetEnumerator()方法,获得一个实现了IEnumerator的类,这个类中有Current, MoveNext等foreach语句必要的调用方法。这里,我们把实现了IEnumerable和IEnumerator的类看作使用foreach语句的工具类,即People和PersonEnum看作工具,实际数据Person看作被操作类型,如下:

更改People类实现IEnumerable接口。

public class People : IEnumerable
{
    private Person[] persons;
    public People(Person[] persons)
    {
        this.persons = new Person[persons.Length];
        for(int i = 0; i < persons.Length; i++)
        {
            this.persons[i] = persons[i];
        }
    }

    // implement the interface method explicitly
    IEnumerator IEnumerable.GetEnumerator()
    {
        return new PersonEnum(this.persons);
    }
} 

PersonEnum是实现了IEnumerator接口的由IEnumerable返回的类。实现PersonEnum类如下:

public class PersonEnum : IEnumerator
{
    private Person[] persons;
    private int position = -1;

    public PersonEnum(Person[] persons)
    {
        this.persons = persons;
    }

    public bool MoveNext()
    {
        position++;
        return position < this.persons.Length;
    }

    // return type is object
    object IEnumerator.Current
    {
        get { return Current; }
    }

    public void Reset()
    {
        position = -1;
    }

    public Person Current
    {
        get
        {
            try
            {
                return this.persons[position];
            }
            catch(IndexOutOfRangeException)
            {
                return null;
            }
        }
    }
}

查看使用效果:

using System
public sealed class Program
{
    static void Main(string[] args)
    {
        Person[] persons = new Person[3];
        persons[0] = new Person("Owen", 22);
        persons[1] = new Person("Vincent", 21);
        persons[2] = new Person("Ricy", 20);

        People people = new People(persons);
        foreach (Person person in people)
            Console.WriteLine(person);
    }
}

运行结果

使用IEnumerable和IEnumerator,由于没有确定被操作的类型,使得操作对象为object,如Current属性的返回值,

object IEnumerator.Current

这使得类型不安全,为了确定类型,建议使用泛型化的IEnumerable和IEnumerator。

注意,实现接口IEnumerable和IEnumerator与实现接口IEnumerable和IEnumerator的类MyEnumberable是否需要泛型化,可以作出如下讨论。

public class MyEnumerable : IEnumerable

这种情况,使得MyEnumerable类正如上述实例中的People类,操作的只能是一种确定的类型(Person)。如果需要扩展被操作的类型,工具类People和PersonEnum操作其他类型,可以做如下泛型化:

public class People : IEnumerable
public class PersonEnum : IEnumerator

代码如下:

using System;
using System.Collections;
public class People<T> : IEnumerable
{
    // type to be operate could be any type
    private T[] persons;
    public People(T[] persons)
    {
        this.persons = new T[persons.Length];
        for (int i = 0; i < persons.Length; i++)
        {
            this.persons[i] = persons[i];
        }
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return new PersonEnum<T>(this.persons);
    }
}

更改PersonEnum:

using System;
using System.Collections;
public class PersonEnum<T> : IEnumerator
{
    private T[] persons;
    private int position = -1;

    public PersonEnum(T[] persons)
    {
        this.persons = persons;
    }

    public bool MoveNext()
    {
        position++;
        return position < this.persons.Length;
    }

    object IEnumerator.Current
    {
        get { return Current; }
    }

    public void Reset()
    {
        position = -1;
    }

    public T Current
    {
        get
        {
            try
            {
                return this.persons[position];
            }
            catch (IndexOutOfRangeException)
            {
                return default(T);
            }
        }
    }
}

注意泛型化之后,Current的catch块中返回default(T),值类型返回0,引用类型返回null。
检查效果:

using System;
public sealed class Program
{
    static void Main(string[] args)
    {
        Person[] persons = new Person[3];
        persons[0] = new Person("Owen", 22);
        persons[1] = new Person("Vincent", 21);
        persons[2] = new Person("Ricy", 20);

        People people = new People(persons);
        foreach (Person person in people)
            Console.WriteLine(person);

        string[] namelist = new string[3];
        namelist[0] = "Owen";
        namelist[1] = "Vincent";
        namelist[2] = "Ricy";

        People<string> names = new People<string>(namelist);
        foreach (string name in names)
            Console.WriteLine(name);
    }
}

运行结果:
运行结果

若MyEnumerable类实现IEnumerable,其自身不做泛型化,

public People : IEnumerable

这样的类型定义直接导致报错,

the type or namespace name ‘T’ could not be found

所以,People必须泛型化。

public People : IEnumerable

版权声明:本文为博主原创文章,未经博主允许不得转载。

利用 IEnumerable接口 实现自定义类型的集合的foreach遍历

IEnumerable 接口公开枚举数,该枚举数支持在非泛型集合上进行简单迭代。方法:    GetEnumerator  返回一个循环访问集合的枚举数。    GetEnumerator  C#语法...
  • zhangqiang0921
  • zhangqiang0921
  • 2010年11月30日 11:18
  • 1679

对于集合的遍历操作,可以使用 itrator foreach for 循环,下面比较一下各自的优缺点

List list = new ArrayList(); list.add("11"); list.add("11"); list.add("33");...
  • learningcsdn
  • learningcsdn
  • 2016年06月28日 16:00
  • 803

Java学习之容器上(Collection接口常用方法,Iterator接口,使用foreach循环遍历Collection集合元素,Set集合通用知识(Hashset类,hashcode()与Lin

1.容器API的类图结构如下:     JAVA的集合类是一种特别有用的工具类,它可以用于存储数量不等的多个对象,并可以实现常用数据结构,如栈,队列等,除此之外,JAVA集合还可用于保存具有...
  • GarfieldEr007
  • GarfieldEr007
  • 2016年12月18日 11:06
  • 1249

对数据遍历的三种方式之foreach 、for与Iterator

对数据的遍历方式有三种:for循环遍历、增强for循环遍历、Iterator迭代器遍历。 增强for循环 增强for循环是JDK1.5版本后出来的一个高级for循环,是用来专门遍历数组和集合...
  • dsj15831653282
  • dsj15831653282
  • 2016年12月01日 20:48
  • 2189

c#创建自定义集合类并使之支持使用foreach迭代

各种集合都可以是foreach来实现迭代访问,下面我们自定义集合类来实现foreach迭代访问 using System; using System.Collections.Generic; cla...
  • jialeheyeshu
  • jialeheyeshu
  • 2016年08月28日 20:26
  • 824

Java数组和foreach遍历循环

数组由多个元素组成,每个元素都具有相同数据类型。一维数组int intArrayOne[]; //定义一个一维数组 int intArrayTwo[] = {1,2,3,4,5}; //静态初始化一个...
  • a_good_programer
  • a_good_programer
  • 2017年04月19日 20:28
  • 3333

foreach循环遍历二维数组

array(3) {   [0]=>   array(8) {   ["degreesid"]=>   string(1) "1"   ["degreeid"]=>   str...
  • A9925
  • A9925
  • 2014年12月23日 14:43
  • 2991

c#中foreach的使用

循环语句是编程的基本语句,在C#中除了沿用C语言的循环语句外,还提供了foreach语句来实现循环。那么我要说的就是,在循环操作中尽量使用foreach语句来实现。为了来更好地说明为什么要提倡使用fo...
  • u013230291
  • u013230291
  • 2017年04月18日 09:50
  • 730

MyBatis的foreach语句详解,可解决界面传输的数组对象参数问题

mybatis的mapper文件中关于单个传入array,map,list对象数组集合
  • xy_xiaomo
  • xy_xiaomo
  • 2016年08月29日 11:16
  • 1905

for和foreach那个效率更高?原因是什么?

写这篇文章的原因主要是在开发过程中突然有以下几个疑问,特抽出时间深度探究一下,以加深自身对php的理解。 1、作为一名phper,for和foreach循环遍历几乎每天都在使用,那么这两种遍历方式哪一...
  • bk_guo
  • bk_guo
  • 2017年06月16日 10:47
  • 3215
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:自定义类型使用foreach循环
举报原因:
原因补充:

(最多只允许输入30个字)