自定義型別使用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語句必要的呼叫方法
更改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