1. 程式人生 > >自定義型別使用foreach迴圈

自定義型別使用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