淺談C#數組(二)
可以先瞭解上一篇文章內容C#數組(一)
一.枚舉集合
在foreach
語句中使用枚舉,可以迭代集合中的元素,且無需知道集合中元素的個數。foreach
語句使用一個枚舉器。foreach會調用實現瞭IEnumerable接口的集合類中的GetEumerator()
方法。GetEumerator()
方法返回一個實現IEnumerator
接口的對象枚舉。foreach語句就可以使用IEnumerable
接口迭代集合瞭。
GetEumerator()
方法在IEnumerable
接口中定義。
1.IEnumerator接口
foreach
語句使用IEnumerator
接口的方法和屬性,迭代集合中所有元素。IEnumerator
接口定義瞭Current屬性,來返回光標所在的元素,該接口的MoveNext()
方法移動到集合的下一個元素上,如果有這個元素,該方法就返回true。如果集合不再有更多的元素,該方法就返回false.
這個接口的泛型版本IEnumerator<T>
派生自接口IDisposable
,因此定義瞭Dispose()
方法,來清理枚舉器占用的資源。
2.foreach語句
C#中foreach
語句不會解析為IL代碼中的foreach
語句。C#編譯器會把foreach
語句轉換為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
語句可以輕松的迭代集合,但創建枚舉器需要做大量的工作。C#2.0添加瞭yield語句,以便創建枚舉器。
yield return
語句返回集合的一個元素,並移動到下一個元素。yield break可停止迭代。
下面的例子實現返回兩個字符串:
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語句的方法或屬性也稱為迭代塊。迭代塊必須聲明為返回IEnumerator
或IEnumerable
接口,或者這些接口的泛型版本。這個塊可以包含多條yield return
語句或yield break
語句,但不能包含return
語句。
使用迭代塊,編譯器會生成一個yield類型,其中包含一個狀態機,如下面代碼所示:
yield
類型實現IEnumerator
和IDisposable
接口的方法和屬性。下面的例子可以把yield類型看作內部類Enumerator。外部類的GetEnumerator()方
法實例化並返回一個新的yield類型。在yield類型中,變量state定義瞭迭代的當前位置,每次調用MoveNext()時,當前位置都會改變。MoveNext()
封裝瞭迭代塊的代碼,並設置瞭current
變量的值,從而使Current屬性根據位置返回一個對象。
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
語句會產生一個枚舉器,而不僅僅生成一個包含的項的列表。這個枚舉器通過foreach
語句調用。從foreach中依次訪問每一項,就會訪問枚舉器。這樣就可以迭代大量的數據,而無需一次把所有的數據都讀入內存。
(1).迭代集合的不同方式
可以使用yield return
語句,以不同方式迭代集合。
類MusicTitles
可以用默認方式通過GetEnumerator()方法迭代標題,該方法不必在代碼中編寫,也可以用Reverse()
逆序迭代標題,用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; }
這樣會交替調用Cross()
和Circle()
方法。
二.元組(Tuple)
元組可以合並不同類型的對象。元組起源於函數編程語言,如F#。在.NET Framework
中,元組可用於所有的.Net語言。
.NET Framework
定義瞭8個泛型Tuple類和一個靜態Tuple類,它們用作元組的工廠。不同的泛型Tuple類支持不同數量的元素。如,Tuple<T1>包含一個元素,Tuple<T1,T2>
包含兩個元素。
Tuple<string, string> name = new Tuple<string, string>(“Jochen”, “Rindt”);
元組也可以用靜態Tuple
類的靜態Create()
方法創建。Create()
方法的泛型參數定瞭要實例化的元組類型:
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個參數的Tuple
類定義。最後一個模板參數是TRest,表示必須給它傳遞一個元組。這樣,就可以創建帶任意個參數的元組瞭。
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));
三.結構比較
數組和元組都實現接口IStructuralEquatable
和IStructuralComparable
。這兩個接口不僅可以比較引用,還可以比較內容。這些接口都是顯式實現的,所以在使用時需要把數組和元組強制轉換為這個接口。
IStructuralEquatable
接口用於比較兩個元組或數組是否有相同的內同,IStructuralComparable
接口用於給元組或數組排序。
IStructuralEquatable接口示例:
編寫實現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 }
創建兩個包含相同內容的Person類型的數組:
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 };
由於兩個變量引用兩個不同數組,所以!=返回True:
if (persons1 != persons2) Console.WriteLine("not the same reference");
對於IStructuralEquatable
接口定義的Equals方法,第一個參數是object類型,第二個參數是IEqualityComparer
類型。調用這個方法時,通過傳遞一個實現瞭EqualityComparer<T>
的對象,就可以定義如何進行比較。通過EqualityComparer<T>
類完成IEqualityComparer
的一個默認實現。這個實現檢查T類型是否實現瞭IEquatable
接口,並調用IEquatable.Equals()方
法。如果該類沒有實現IEquatable
接口,就調用Object基類中Equals()方法:
if ((persons1 as IStructuralEquatable).Equals(persons2, EqualityComparer<Person>.Default)) { Console.WriteLine("the same content"); }
元組示例:
Tuple<>
類提供瞭兩個Epuals()
方法:一個重寫瞭Object基類中的Epuals方法,並把object
作為參數,第二個由IStructuralEquatable
接口定義,並把object和IEqualityComparer
作為參數。
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>,
以進行比較。這樣就會調用Object.Equals()方法比較元組的每一項:
if (t1.Equals(t2)) Console.WriteLine("equals returns true");
還可以使用TupleComparer
類創建一個自定義的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 }
到此這篇關於淺談C#數組的文章就介紹到這瞭,更多相關C#數組內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!