自定义类型使用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

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

相关文章推荐

Iterator与Iterable之foreach循环输出自定义类

无意中在网上看到一篇文章,把作者的想法进行了实现并进行了改进。 原文地址http://my.oschina.net/u/1412027/blog/226376 原文内容:      从Java5起,...

自定义标签的开发及使用自定义标签实现迭代foreach循环

在JSP2中,规范简化了标签库的开发,在JSP2中开发自定义标签只需如下三个步骤:    一、开发自定义标签处理类    二、建立一个*.tld文件    三、在JSP文件中使用自定义标签  ...

双向循环链表-模板-自定义类型

#include "stdafx.h" #include #include #include using namespace std; /*** 异常类 ***/ class...

使用foreach循环

  • 2011年03月02日 19:52
  • 184KB
  • 下载

Taglib自定义标签之三简单仿JSTL中带标签体的ForEach循环

上2篇文章分别了介绍了taglib的无属性状态和有带属性状态,但是都是分别都是独立的闭标签,这次带来的是带属性的和带标签体的例子,仿照jstl中的forEach循环构造一个简单的循环标签. Java代...
  • aionbo
  • aionbo
  • 2017年02月09日 21:53
  • 355

自定义msg类型及使用[测试代码]

  • 2017年06月07日 20:12
  • 11KB
  • 下载

黑马程序员_学习日记40_605基础加强(自定义泛型、装拆箱、foreach、文件操作、字符串与byte数组转换)

1、 自定义泛型 l        泛型的目的也是为了代码重用(算法重用) classMyListGeneric {         T[] arr = new T[100];     ...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:自定义类型使用foreach循环
举报原因:
原因补充:

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