自定义类型使用foreach循环

示例代码:

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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值