[.net]數組
在C語言中,數組是比較簡單,也使用比較多的一種基礎的數據結構。常用的有一維數組,二維數組等。但是在C#中,使用最多的是List,Dictionary等一些集合類,因為用他們來操作同類型的數據,比數組更加方便。當然,C#的數組Array也通過實現一些接口,提供了訪問和操作數據的一些便捷方法。而在C語言中,都是比較不容易實現或者使用不方便。這也就是C#作為一門面向對象的語言的好處,雖然由此帶來的性能損失,尤其在做算法相關的問題的時候。但退一步講,在如今硬件資源比較豐富的情況下,日常場景中,這種性能損失還是可以接受的。
接下來就聊聊C#種數組的那些事兒。
1.數組基本概念:
學習C#數組,首先需要明確一點,C#的數組是引用類型,即使數據基類型是值類型。作為引用類型,自然就會有類型對象指針,同步索引塊,開銷字段(overhead)等。
2.C#數組分類:
C#支持多維數組,也支持交錯數組。并且他們都可以是非0基的。所謂非0基,是指數組第一個元素的索引不是從0開始。相應的,.Net CLR本身支持非0基數組,但是CLS規范規定所有數組都應該是0基數組,主要是為了兼容性和跨平臺性,畢竟.Net家族除了C#還有VB,F#,C++(托管的)等語言。
概念解釋:
一維0基數組:它又稱為SZ數組(Single-dimension,Zero-based)或者向量。它的性能是最好的,因為編譯器可以為他生成特殊的IL指令,實現JIT編譯時候的優化,諸如以下指令(newarr,ldelem,ldelema,ldlen,stelem)。
使用如下:
int[] nums=new int[10];
多維數組:又稱矩形數組。從行列的角度來考慮的話,它的每一行的列數都必須時相等的。
使用如下:
int[,] nums=new int[10,10];
交錯數組:用行列的角度來考慮的畫,它的每一行的列數可以不相同。
int[][] nums=new int[10][]; for(int i=0;i<10;i++) { nums[i]=new int[10];//交錯數組,各數組長度可不一樣 }
交錯數組這個定義方式,非常類似于C語言中的動態多維數組,在C語言中,我們常用以下方法來開辟動態的二維數組
int i; int** nums=(int**) malloc(sizeof(int*)*10); for(i=0;i<10;i++) { nums[i]=(int*)malloc(sizeof(int)*10);//這兒10是個實例,更具需求設置 }
二者的定義的確有相似的地方,但應該注意到他們在本質上時不同的。
3.數組索引:
數組的索引是數組的一個很核心的概念,因為我們對數組的訪問一般都是通過索引來實現的,但對索引的不當處理,可能導致程序終止。
在C語言中,數組的索引控制都需要程序員來實現。如果發生索引超出數組范圍,導致越界訪問內存數據,甚至修改內存數據,這些都是不可察覺的,但無疑它為系統的奔潰留下了伏筆;
在C#中,由于CLR要保證數據的安全,CLR在運行代碼的時候,如果檢測到數組索引超出范圍,會立即拋出System.IndexOutOfRangeException異常,從而終止錯誤的繼續。當然也可以使用unsafe關鍵字,來忽略CLR的這種默認行為。
C#支持非0基數組,意味著數組可以的索引可以從非0的數開始,可以使用Arrary.CreatInstance()方法來創建下限非零數組。
4.數組元素初始化:
C#中為數組的初試化提供了一些甜蜜的語法糖。通常我們用以下幾種方式來初始化一個數組。
int[] age=new int[]{10,20,30};//大括號中的數據項稱為數組初始化器(arrar,initializer) var age=new int[]{10,20,30} var age=new [] {10,20,30};//隱式類型推斷
C#的隱式類型推斷為創建一個數組提供了良好的支持。
5.數組轉型:
類型轉換是編程種很常見的一件事兒,我們把數組的類型轉換又稱為數組協變性(array convariance),在數組的轉型的時候,需要滿足以下幾點條件:
①數組維數相同
②基類型存在隱式或顯式類型轉化
需要注意以下幾點:
CLR不允許值類型數組轉型為其他任何類型數組,但可以通過Array.Copy()變通實現。
Array.Copy():支持裝箱,拆箱,以及加寬基元類型(比如int到double的轉變),但它是淺拷貝,即如果數組基類型是引用類型,則只復制其引用。
System.Buffer.BliockCopy() 支持基元類型,但不具有轉型能力
System.Array.ConstrainnedCopy() 不支持裝箱、拆箱和向下類型轉化(父類到子類的轉換),但它是可靠的,數據安全的,要么成功復制一個數組,要么不會修改目標數組的任何數據
6.數組接口:
數組實現了一些常用的接口,相當于給數組插上了訪問的翅膀。
System.Array數組基類實現了ICollection,IEnumerable,IList的非泛型版本,因為多維數組和非0基數組的原因,沒有實現泛型版本(原因我也不太清楚)
CLR單獨為一維0基數組實現了泛型版本接口,包括其基類型,但是System.ValueType和Object除外
7.數組做參數:
在使用中,不可避免的要把數組作為一個參數來使用,那么在使用的時候,需要注意以下幾點
①作為方法實參時傳遞的是引用;
②如果需要返回一個數組類型的字段,建議使用Array.Copy(),返回一個從該字段復制的數組,這么做是為了保證OOP的開放封閉原則,因為通常字段是私有的,不能被外部方法修改的和使用的;
③對于返回數組的方法,如果數組元素個數為0,仍不建議返回一個null,而是應該返回一個空的數組,這么做的目的在于對于方法返回參數的使用這來說,免去了null判斷,防止產生空引用異常的錯誤。
8.數組原理:
實際在CLR內部只支持兩種類型數組:一維0基數組 和 下限未知的一維或多維數組;
一維0基數組:可以使用特殊的IL指令(newarr,ldelem,ldelema,ldlen,stelem),讓編譯器產生優化代碼。比如索引檢查發生的時刻提前,循環判斷的提前的。
下限未知的一維或多維數組:每次數組訪問前驗證索引有效性
一些使用C#數組的建議:
①用交錯數組代替矩形數組
②Unsafe關鍵字訪問數組時可關閉索引上下限檢查,支持常見的值類型和值類型結構(在fixed語句中)
③可以在線程棧上分配數組,利用stackalloc語句,類似于C語言alloca語句 ,條件是必須為一維0基數組,數組基類型為值類型
內容均來自于Jeffrey Richter的《CLR Via C#》第四版一書,以及個人總結和心得。學習深入了解C#,建議從這本書開始學習。

浙公網安備 33010602011771號