《C#進階編程》【第六章】數組,

來源:互聯網
上載者:User

《C#進階編程》【第六章】數組,
       為瞭解決大量的同類型元素,於是數組就孕育而生了。數組是具有一定循序關聯性的若干對象的集合體,一維數組可以看作是定長的線性表。反之,n為的數組可以看作線性表的推廣。從儲存結構上來看,數組是一段連續的儲存空間。現在我們看看在C#中的數組:

1、普通數組
       在C#中普通數組又可以分為一維數組、多維陣列和鋸齒數組。
<1>一維數組
       我們現在先看看一維數組的聲明文法:

類型[] 變數名;

       知道怎麼聲明了,現在我們繼續看看數組的初始化吧,在C#中有4種初始化的方式:

//n為數組長度,an為數組內部元素類型[] 數組名 = new 類型[n];  //為數組分配記憶體,但是沒有賦初值(vs會自動為其賦初值為0)類型[] 數組名 = new 類型[n]{a1, a2, …, an}//初始化,並賦初實值型別[] 數組名 = new 類型[]{a1, a2 ,…, an}//還可以不指定數組長度,編譯器會自動統計元素個數類型[] 數組名 = {a1, a2,…, an}//C風格的初始化並賦值

      訪問數組時,以”數組名[i]”的方式訪問第 i-1個元素。如果不知道數組的長度,可以使用Length屬性。
      注意:如果數組中的元素類型是參考型別,就必須為每個元素分配記憶體。在C#中”類型[]”是一個不可分割的整體,”類型[]”可以看成是 數群組類型。
<2>多維陣列
      看完一維數組,現在我們推廣到多維陣列,聲明文法:

類型[,] 數組名;//二維數群組類型[,,] 數組名;//三維數組

      相信你也發現了吧,方括弧內的逗號數 + 1 就是數組的維數。我們以二維數組為例,來看看多維陣列的初始化:

int[,] arr = new int[2,3]{{1,2,3},{4,5,6}};

      借這個例子我想說明多維陣列和一維數組初始化的區別就是,多維陣列初始化時,每一維度都必須使用大括弧括起來。其餘的和一維數組初始化方法一樣。

<3>鋸齒數組
      在使用多維陣列的過程中,我們有時並不需要每一維度都一樣,於是我們就引入了鋸齒數組。(在C++中的Vector也有類似的功能)。上一幅圖說明二維數組與鋸齒數組的區別:

     現在我們看看他的聲明文法:

類型[][] 數組名 = new 類型[n][];//n為鋸齒數組的維度,後一個方括弧為空白

     我們用一個具體執行個體來說看看他的使用方法:

int[][] Testarray = new int[2][];Testarray[0] = new int[3]{1,2,3};//當然也可以先不賦初值,建議都先賦初值Testarray[1] = new int[4]{1,2,3,4};

      這時候有些人可能會有疑問,每一維度長度不同,那樣怎麼簡單的遍曆整個數組呢?這時Length屬性就可以發揮它的優勢了。我們以上述的為例:

for(int i = 0; i < Testarray.Length; i++){for(int j = 0; j < Testarray[i].Length; j++){//TODO:}}

<4>數組作為參數
       既然我們將數組看成一個類型,那麼它自然也是可以作為參數傳遞給方法,也可以從方法中返回。C#數組還支援協變,但是數組協變只能用於參考型別,不能用於值類型。
<5>數組段ArraySegment<T>
      ArraySegment<T>可以和數組之間建立一個映射,直接針對數組的某一片段進行操作,其操作後的結果會直接反映在數組上,反之數組上的變化也會反映到數組段上。我們來看看具體的使用吧:

ArraySegment<int> Test = new ArraySegment<int>(arr, 1, 4);

     上述例子,表示Test,從arr[1]開始引用了4個元素。Test.Offset就表示第一個引用的元素,也就是arr[1]。

2、Array類
      我們之前使用方括弧聲明數組,實際上就是隱式的使用了Array類。換一個角度看,我們使用的,例如:int[], double[] 我們都可以把他們看成是派生自Array的子類,這樣我們可以使用Array為數組定義方法和屬性。
<1>建立數組
      Array是抽象類別,所以不能執行個體化。但是可以使用靜態方法CreateInstance()來建立數組。因為CreateInstance()有多個重載版本,我們就其中一個為例:

//建立一個int型,長度為5的數組,TestArray Test = Array.CreateInstance(typeof(int), 5);//我們將Test[3]的值,賦值為5Test.SetValue(5, 3);//我們要返回 Test[3]的值Test.GetValue(3);//將它變為int[]的數組int[] T1 = (int[])Test;

<2>複製數組
       我們可以使用Clone()方法來複製數組,但是如果數組是參考型別那麼就只能複製對方的引用。如果數組是值類型,那麼才能完整的將對方複製過來。我們還可以使用Copy()方法建立淺表副本。
注意:Clone()和Copy()最大的區別:Copy()方法必須使用與原數組相同階數且有足夠的元素空間,但是Cone()方法會建立一個和原數組等大的數組。
<3>排序
         Array類還提供了QuickSort排序演算法。使用Sort()方法可以對數組進行排序。但是使用Sort()方法需要實現IComparable介面(.Net已經為基礎資料型別 (Elementary Data Type)實現了IComparable介面,預設從小到大)。對於自訂類型,我們就必須實現IComparable<T>介面,這個介面只用一個方法CompareTo()。如果兩者相等,就返回0。如果該執行個體在參數對象的前面,就返回小於0的值,反之就返回大於0的值。
       我們也可以是通過實現IComparer<T>和IComparer介面。我們現在著重看看這個和IComparable介面的區別:
       ①IComparable在要比較對象的類中實現,可以比較該對象和另一個對象。
       ②IComparer要在單獨一個類中實現,可以比較任意兩個對象。
3、枚舉
      在foreach語句中使用枚舉,可以迭代集合中的元素,而且不需要知道集合中的元素個數。foreach語句使用了一個列舉程式,我們需要實現IEnumerable介面就可以使用foreach來迭代集合。(數組和集合已經預設實現了IEnumerable介面)。
<1>foreach原理 和 IEnumerator 介面
     foreach使用了IEnumerator介面的方法和屬性。

//per為Person類的對象foreach(var p in per){Consle.WriteLine(p);}

    C#編譯器會將這段代碼解析為

IEnumerator<Person>  em = per.GetEnumerator();while(em.MoveNext()){Person p = em.Current;Console.WriteLine(p);}

      IEnumerator介面的MoveNext()方法作用是:移動到集合的下一個元素,如果有則返回true,否則為false。Current屬性為當前的值。

<2>yield語句
        由於建立列舉程式的工作過於繁瑣,於是我們就引入了yield語句,來協助我們減輕工作量,yield return 返回集合的一個元素,yield break 可停止迭代。
下面我們可以通過一個簡單的例子,來瞭解yield的用法:

public class TFoo{public IEnumerator<string> GetEnumerator(){yield return “Hello”;yield return “World”;}}

       現在我們通過foreach迭代集合

int cnt = 0;//我們用這個來看看集合在foreach中迭代了幾次var Test = new TFoo();foreach(var s in Test){cnt++;Console.WriteLine(s);}

        最後我們可以得到cnt = 2且會輸出Hello World。通過這個執行個體我們就可以大致的瞭解yield的工作方式。在之前學習泛型的時候我們在鏈表中已經使用過一次yield了。
注意:yield不能出現在匿名方法中
4、元組(Tuple)
       數組是為了處理大量的同類型資料,那麼我們要對不同類型的資料可以用什麼類似的方法處理嗎?當然,為此我們就引入了Tuple類。.Net中定義了8個泛型Tuple類,和一個靜態Tuple。例如:Tuple<T1>包含一個類型為T1的元素,Tuple<T1,T2>則包含兩個類型為T1,T2的元素,依次類推。
如果元組元素超過8個那麼第8個就可以使用Tuple類定義,例如:

Tuple<T1, T2, T3, T4, T5, T6, T7, TRest>//TRest為另一個元組

我們通過這樣的方法就可以建立帶任意多個的元組了。
我們使用Create()方法建立元組,例如:

var Test = Tuple.Create<int,int>(2,5);

5、結構比較
        數組和元組都實現介面IStructuralEquatable 和 IStructuralComparable。這兩個介面不僅僅可以用來比較引用,還可以比較內容。因為這些介面是顯示實現的,所以在使用時需要把數組和元組強制轉化為這個介面。
       IStructuralEquatable介面用於比較兩個數組或元組是否具有相同的內容。
       IStructuralComparable介面用於給數組或者元組排序。
       我們用一個執行個體來簡單的認識IStructuralEquatable介面的用法:

public class Test{    public int Id { get; set; }    public override bool Equals(object obj)    {        if (obj == null)            return base.Equals(obj);        else            return this.Id == (obj as Test).Id;    }}

       我們現在再定義兩個類內容相同的類對象t1,t2。

var t1 = new Test[2]{new Test{Id = 2}, new Test{Id = 3}};var t2 = new Test[2]{new Test{Id = 2}, new Test{Id = 3}};
       如果我們直接使用“==”或者“!=”比較那麼編譯器只會把我們的引用進行比較。這是我們就需要用到IStructuralEquatable介面了。
(t1 as IStructuralEquatable).Equals(t2, EqualityComparer<Test>.Default)

       這樣我們比較的就是t1,t2的內容了,因為是內容的比較所以它們將會返回True。EqualityComparer<Test>.Default調用的是Test預設的Equals()方法,所以我們只要重寫它預設的Equals()方法,給重寫的Equals()方法類內容比較的規則,那麼我們就可以比較類對象間,是否具有相同的內容。
       對於元組e1,e2,我們直接使用e1.Equals(e2)我們就可以比較元組間的內容,但是同樣的如果使用比較子“==”和“!=”我們還是只能比較他們的引用。

(如有錯誤,歡迎指正,轉載請註明出處)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.