淺談C#數(shù)組(二)
可以先了解上一篇文章內(nèi)容C#數(shù)組(一)
一.枚舉集合
在foreach語句中使用枚舉,可以迭代集合中的元素,且無需知道集合中元素的個數(shù)。foreach語句使用一個枚舉器。foreach會調(diào)用實現(xiàn)了IEnumerable接口的集合類中的GetEumerator()方法。GetEumerator()方法返回一個實現(xiàn)IEnumerator接口的對象枚舉。foreach語句就可以使用IEnumerable接口迭代集合了。
GetEumerator()方法在IEnumerable接口中定義。
1.IEnumerator接口
foreach語句使用IEnumerator接口的方法和屬性,迭代集合中所有元素。IEnumerator接口定義了Current屬性,來返回光標(biāo)所在的元素,該接口的MoveNext()方法移動到集合的下一個元素上,如果有這個元素,該方法就返回true。如果集合不再有更多的元素,該方法就返回false.
這個接口的泛型版本IEnumerator<T>派生自接口IDisposable,因此定義了Dispose()方法,來清理枚舉器占用的資源。
2.foreach語句
C#中foreach語句不會解析為IL代碼中的foreach語句。C#編譯器會把foreach語句轉(zhuǎn)換為IEnumerator接口的方法和屬性。
Person[] persons = {
new Person { FirstName="Damon", LastName="Hill" },
new Person { FirstName="Niki", LastName="Lauda" },
new Person { FirstName="Ayrton", LastName="Senna" },
new Person { FirstName="Graham", LastName="Hill" }
};
foreach (Person p in persons)
{
Console.WriteLine(p);
}
foreach語句會解析為下面的代碼:
IEnumerator<Person> enumerator = persons.GetEumerator();
while(enumerator.MoveNext())
{
Person p = enumerator.Current;
Console.WriteLine(p);
}
3.yield語句
在C#2.0之前,foreach語句可以輕松的迭代集合,但創(chuàng)建枚舉器需要做大量的工作。C#2.0添加了yield語句,以便創(chuàng)建枚舉器。
yield return 語句返回集合的一個元素,并移動到下一個元素。yield break可停止迭代。
下面的例子實現(xiàn)返回兩個字符串:
public class HelloCollection
{
public IEnumerator<string> GetEnumerator()
{
yield return "Hello";
yield return "World";
}
}
客戶端代碼:
var helloCollection = new HelloCollection();
foreach (string s in helloCollection)
{
Console.WriteLine(s);
}
包含yield語句的方法或?qū)傩砸卜Q為迭代塊。迭代塊必須聲明為返回IEnumerator或IEnumerable接口,或者這些接口的泛型版本。這個塊可以包含多條yield return語句或yield break語句,但不能包含return語句。
使用迭代塊,編譯器會生成一個yield類型,其中包含一個狀態(tài)機,如下面代碼所示:
yield類型實現(xiàn)IEnumerator和IDisposable接口的方法和屬性。下面的例子可以把yield類型看作內(nèi)部類Enumerator。外部類的GetEnumerator()方法實例化并返回一個新的yield類型。在yield類型中,變量state定義了迭代的當(dāng)前位置,每次調(diào)用MoveNext()時,當(dāng)前位置都會改變。MoveNext()封裝了迭代塊的代碼,并設(shè)置了current變量的值,從而使Current屬性根據(jù)位置返回一個對象。
public class HelloCollection
{
public IEnumerator<string> GetEnumerator()
{
return new Enumerator(0);
}
public class Enumerator:IEnumerator<string>,IEnumerator,IDisposable
{
private int state;
private string current;
public Enumerator(int state)
{
this.state = state;
}
bool System.Collections.IEnumerator.MoveNext()
{
switch(state)
{
case 0:
current="hello";
state =1;
return true;
case 1:
current="world";
state =2;
return true;
case 2:
break;
}
return false;
}
void System.Collection>IEnumerator.Reset()
{
throw new NotSupportedException();
}
string System.Collections.Generic.IEnumerator<string>.Current
{
get
{
return current;
}
}
object System.Collections.IEnumerator.Current
{
get
{
return current;
}
}
void IDisposable.Dispose()
{}
}
}
yield語句會產(chǎn)生一個枚舉器,而不僅僅生成一個包含的項的列表。這個枚舉器通過foreach語句調(diào)用。從foreach中依次訪問每一項,就會訪問枚舉器。這樣就可以迭代大量的數(shù)據(jù),而無需一次把所有的數(shù)據(jù)都讀入內(nèi)存。
(1).迭代集合的不同方式
可以使用yield return語句,以不同方式迭代集合。
類MusicTitles可以用默認方式通過GetEnumerator()方法迭代標(biāo)題,該方法不必在代碼中編寫,也可以用Reverse()逆序迭代標(biāo)題,用Subset()方法迭代子集合:
public class MusicTitles
{
string[] names = {
"Tubular Bells", "Hergest Ridge",
"Ommadawn", "Platinum" };
public IEnumerator<string> GetEnumerator()
{
for (int i = 0; i < 4; i++)
{
yield return names[i];
}
}
public IEnumerable<string> Reverse()
{
for (int i = 3; i >= 0; i--)
{
yield return names[i];
}
}
public IEnumerable<string> Subset(int index, int length)
{
for (int i = index; i < index + length;i++)
{
yield return names[i];
}
}
}
客戶端代碼:
var titles = new MusicTitles();
foreach (var title in titles)
{
Console.WriteLine(title);
}
Console.WriteLine();
Console.WriteLine("reverse");
foreach (var title in titles.Reverse())
{
Console.WriteLine(title);
}
Console.WriteLine();
Console.WriteLine("subset");
foreach (var title in titles.Subset(2, 2))
{
Console.WriteLine(title);
}
(2).用yield return 返回枚舉器
public class GameMoves
{
private IEnumerator cross;
private IEnumerator circle;
public GameMoves()
{
cross = Cross();
circle = Circle();
}
private int move = 0;
const int MaxMoves = 9;
public IEnumerator Cross()
{
while (true)
{
Console.WriteLine("Cross, move {0}", move);
if (++move >= MaxMoves)
yield break;
yield return circle;
}
}
public IEnumerator Circle()
{
while (true)
{
Console.WriteLine("Circle, move {0}", move);
if (++move >= MaxMoves)
yield break;
yield return cross;
}
}
}
客戶端代碼:
var game = new GameMoves();
IEnumerator enumerator = game.Cross();
while (enumerator.MoveNext())
{
enumerator = enumerator.Current as IEnumerator;
}
這樣會交替調(diào)用Cross()和Circle()方法。
二.元組(Tuple)
元組可以合并不同類型的對象。元組起源于函數(shù)編程語言,如F#。在.NET Framework中,元組可用于所有的.Net語言。
.NET Framework定義了8個泛型Tuple類和一個靜態(tài)Tuple類,它們用作元組的工廠。不同的泛型Tuple類支持不同數(shù)量的元素。如,Tuple<T1>包含一個元素,Tuple<T1,T2>包含兩個元素。
Tuple<string, string> name = new Tuple<string, string>("Jochen", "Rindt");
元組也可以用靜態(tài)Tuple類的靜態(tài)Create()方法創(chuàng)建。Create()方法的泛型參數(shù)定了要實例化的元組類型:
public static Tuple<int, int> Divide(int dividend, int divisor)
{
int result = dividend / divisor;
int reminder = dividend % divisor;
return Tuple.Create<int, int>(result, reminder);
}
可以用屬性Item1和Item2訪問元組的項:
var result = Divide(5, 2);
Console.WriteLine("result of division: {0}, reminder: {1}", result.Item1, result.Item2);
如果元組包含的項超過8個,就可以使用帶8個參數(shù)的Tuple類定義。最后一個模板參數(shù)是TRest,表示必須給它傳遞一個元組。這樣,就可以創(chuàng)建帶任意個參數(shù)的元組了。
var tuple = Tuple.Create<string, string, string, int, int, int, double, Tuple<int, int>>( "Stephanie", "Alina", "Nagel", 2009, 6, 2, 1.37, Tuple.Create<int, int>(52, 3490));
三.結(jié)構(gòu)比較
數(shù)組和元組都實現(xiàn)接口IStructuralEquatable和IStructuralComparable。這兩個接口不僅可以比較引用,還可以比較內(nèi)容。這些接口都是顯式實現(xiàn)的,所以在使用時需要把數(shù)組和元組強制轉(zhuǎn)換為這個接口。
IStructuralEquatable接口用于比較兩個元組或數(shù)組是否有相同的內(nèi)同,IStructuralComparable接口用于給元組或數(shù)組排序。
IStructuralEquatable接口示例:
編寫實現(xiàn)IEquatable接口的Person類,IEquatable接口定義了一個強類型化的Equals()方法,比較FirstName和LastName的值:
public class Person : IEquatable<Person>
{
public int Id { get; private set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public override string ToString()
{
return String.Format("{0}, {1} {2}", Id, FirstName, LastName);
}
public override bool Equals(object obj)
{
if (obj == null)
return base.Equals(obj);
return Equals(obj as Person);
}
public override int GetHashCode()
{
return Id.GetHashCode();
}
#region IEquatable<Person> Members
public bool Equals(Person other)
{
if (other == null)
return base.Equals(other);
return this.FirstName == other.FirstName && this.LastName == other.LastName;
}
#endregion
}
創(chuàng)建兩個包含相同內(nèi)容的Person類型的數(shù)組:
var janet = new Person { FirstName = "Janet", LastName = "Jackson" };
Person[] persons1 = { new Person { FirstName = "Michael", LastName = "Jackson" }, janet };
Person[] persons2 = { new Person { FirstName = "Michael", LastName = "Jackson" }, janet };
由于兩個變量引用兩個不同數(shù)組,所以!=返回True:
if (persons1 != persons2)
Console.WriteLine("not the same reference");
對于IStructuralEquatable接口定義的Equals方法,第一個參數(shù)是object類型,第二個參數(shù)是IEqualityComparer類型。調(diào)用這個方法時,通過傳遞一個實現(xiàn)了EqualityComparer<T>的對象,就可以定義如何進行比較。通過EqualityComparer<T>類完成IEqualityComparer的一個默認實現(xiàn)。這個實現(xiàn)檢查T類型是否實現(xiàn)了IEquatable接口,并調(diào)用IEquatable.Equals()方法。如果該類沒有實現(xiàn)IEquatable接口,就調(diào)用Object基類中Equals()方法:
if ((persons1 as IStructuralEquatable).Equals(persons2, EqualityComparer<Person>.Default))
{
Console.WriteLine("the same content");
}
元組示例:
Tuple<>類提供了兩個Epuals()方法:一個重寫了Object基類中的Epuals方法,并把object作為參數(shù),第二個由IStructuralEquatable接口定義,并把object和IEqualityComparer作為參數(shù)。
var t1 = Tuple.Create<int, string>(1, "Stephanie");
var t2 = Tuple.Create<int, string>(1, "Stephanie");
if (t1 != t2)
Console.WriteLine("not the same reference to the tuple");
這個方法使用EqualityComparer<object>.Default獲取一個ObjectEqualityComparer<object>,以進行比較。這樣就會調(diào)用Object.Equals()方法比較元組的每一項:
if (t1.Equals(t2))
Console.WriteLine("equals returns true");
還可以使用TupleComparer類創(chuàng)建一個自定義的IEqualityComparer
TupleComparer tc = new TupleComparer();
if ((t1 as IStructuralEquatable).Equals(t2, tc))
{
Console.WriteLine("yes, using TubpleComparer");
}
class TupleComparer : IEqualityComparer
{
#region IEqualityComparer Members
public new bool Equals(object x, object y)
{
bool result = x.Equals(y);
return result;
}
public int GetHashCode(object obj)
{
return obj.GetHashCode();
}
#endregion
}
到此這篇關(guān)于淺談C#數(shù)組的文章就介紹到這了,更多相關(guān)C#數(shù)組內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
c#使用S22.Imap收劍靈激活碼郵件代碼示例(imap收郵件)
一個IMAP收發(fā)郵件的類庫S22.IMAP,方便易用,下面來個例子可以收劍靈激活碼郵件2013-12-12

