數組是允許將多個數據項當作一個集合來處理的機制。CLR支持一維數組、多維數組和交錯數據(即由數組構成的數組)。所有數組類型都隱式地從System.Array抽象類派生,後者又派生自System.Object。這意味著數組始終是引用類型,是在托管堆上分配的。在你應用程序的變量或字段中,包含的是對數組的引用,而不是包含數組本身的元素。下面的代碼更清楚的說明了這一點:
Int32[] myIntegers; myIntegers = int32[]
C#也支持多維數組。下面演示了幾個多維數組的例子:
Double[,] myDoubles = Double[, String[,,] myStrings = String[,,];
Point[][] myPolygons = Point[ myPolygons[] = Point[ myPolygons[] = Point[ myPolygons[] = Point[ (Int32 x = ; x < myPolygons[].Length; x++
前面展示了如何創建一個數組對象,以及如何初始化數組中的元素。C#允許用一個語句來同時做兩件事。例如:
String[] names = String[] { , };
names = [] { , , };
組的類型。在本例中,編譯器發現兩個String和一個null。由於null可隱式轉型成為任意引用類型(包括String),所以編譯器推斷應該創建和初始化一個由String引用構成的數組。
給定一下代碼:
names = [] { , , };
編譯器是會報錯的,雖然String類和Int32共同基類是Object,意味著編譯器不得不創建Object引用了一個數組,然後對123進行裝箱,並讓最後一個數組元素引用已裝箱的,值為123的一個Int32。但C#團隊認為,隱式對數組 元素進行裝箱是一個代價昂貴的操作,所以要做編譯時報錯。
在C#中還可以這樣初始化數組:
String[] names = { , };
names = { , };
kids = [] { { Name= }, { Name= ( kid
Aidan
Grant
對於元素為引用類型的數組,CLR允許將數組元素從一種類型隱式轉型到另一種類型。為了成功轉型,兩個數組類型必須維數相等,而且從源類型到目標類型,必須存在一個隱式或顯示轉換。CLR不允許將值類型元素的數組轉型為其他任何類型。(不過為了模擬實現這種效果,可利用Array.Copy方法創建一個新數組並在其中填充數據)。下面演示了數組轉型過程:
FileStream[,] fs2dim = FileStream[, Object[,] o2dim = Stream[,] s2dim = String[,] st2dim = Int32[] i1dim = Int32[ Object[] o1dim =
Copy方法還能在復制每一個數組元素時進行必要的類型轉換。Copy方法能執行以下轉換:
1)將值類型的元素裝箱為引用類型的元素,比如將一個Int32[]復制到一個Object[]中。
2)將引用類型的元素拆箱為值類型的元素,比如將一個Object[]復制到Int32[]中。
3)加寬CLR基元值類型,比如將一個Int32[]的元素復制到一個Double[]中。
4)在兩個數組之間復制時,如果僅從數組類型證明不了兩者的兼容性。
在某些情況下,將數組從一種類型轉換為另一種類型是非常有用的。這種功能稱為數據協變性。利用數組協變性時,應該清楚由此帶來的性能損失。
注意:如果只需要把數組中某些元素復制到另一個數組,可以選擇System.Buffer的BlockCopy方法,它的執行速度比Array.Copy方法快。不過,Buffer的BlockCopy方法只支持基元類型,不提供像Array的Copy方法那樣的轉型能力。方法的Int32參數代表的是數組中的字節偏移量,而非元素索引。如果需要可靠的將一個數組中的元素復制到另一個數組,應該使用System.Array的ConstrainedCopy方法,該方法能保證不破壞目標數組中的數組的前提下完成復制,或者拋出異常。另外,它不執行任何裝箱、拆箱或向下類型轉換。
如果像下面這樣聲明一個數組變量:
FileStream[] fsArray;
許多方法都能操作各種集合對象,因為在聲明它們時,使用了IEnumerable,ICollection和IList等參數。可以將數組傳給這些方法,因為System.Array也實現了這三個接口。System.Array之所以實現這些非泛型接口,是因為這些接口將所有元素都視為Systm.Object。然而,最好讓System.Array實現這個接口的泛型形式,提供更好的編譯時類型安全性和更好的性能。
數組作為實參傳給一個方法時,實際傳遞的是對該數組的引用。因此,被調用的方法能修改數組中的元素。如果不想被修改,必須生成數組的一個拷貝,並將這個拷貝傳給方法。注意,Array.Copy方法執行的是淺拷貝。
有的方法返回一個對數組的引用。如果方法構造並初始化數組,返回數組引用是沒有問題的。但假如方法返回的是對一個字段維護的內部數組的引用,就必須決定是否向讓該方法的調用者直接訪問這個數組及其元素。如果是就可以返回數組引用。但是通常情況下,你並不希望方法的調用這獲得這個訪問權限。所以,方法應該構造一個新數組,並調用Array.Copy返回對新數組的一個引用。
如果定義一個返回數組引用的方法,而且該數組不包含元素,那麼方法既可以返回null,又可以放回對包含另個元素的一個數組的引用。實現這種方法時,Microsoft強烈建議讓它返回後者,因為這樣做能簡化調用該方法時需要的代碼。
Appointment[] app = (Int32 a =; a< app.Length; a++ }
Appointment[] app =( app != (Int32 a =; a< app.Length; a++
可以調用數組的靜態CreateInstance方法來動態創建自己的數組。該方法有若干個重載版本,允許指定數組元素的類型、數組的維數、每一維的下限和每一維的元素數目。CreateInstance為數組分配內存,將參數信息保存到數組的內存塊的額外開銷(overhead)部分。然後返回對該數組的一個引用。
CLR內部實際支持兩種不同的數組
1)下限為0的意味數組。這些數組有時稱為SZ數組或向量。
2)下限未知的一維或多維數組。
可執行一下代碼來實際地查看不同種類的輸出
Array a; a = String[ a = Array.CreateInstance((String), Int32[] { }, Int32[] { a = Array.CreateInstance((String), Int32[] { }, Int32[] { a = String[, a = Array.CreateInstance((String), Int32[] { , }, Int32[] { , a = Array.CreateInstance((String), Int32[] { , }, Int32[] { ,
對於多維數組,0基和1基數組會顯示同樣的類型名稱:System.String[,]。在運行時,CLR將對所有多維數組都視為非0基數組。這自然會人覺得應該顯示為System.String[*,*]。但是,對於多維數組,CLR決定不用*符號,避免開發人員對*產生混淆。
訪問一維0基數組的元素比訪問非0基數組或多維數組的元素稍快一些。首先,有一些特殊的IL指令,比如newarr,ldelem,ldelema等用於處理一維0基數組,這些特殊IL指令會導致JIT編譯器生成優化代碼。其次,JIT編譯器知道for循環要反問0到Length-1之間的數組元素。所以,JIT編譯器生成的代碼會在運行時測試所有數組元素的訪問都在數組有效訪問內。
如果很關系性能,請考慮由數組構成的數組(即交錯數組)來替代矩形數組。
下面C#代碼演示了訪問二維數組的三種方式:
Int32 c_numElements = Int32 testCount = Int32[,] a2Dim = Int32[][] aJagged = (Int32 x = ; x < c_numElements; x++= sw = (Int32 test = ; test < testCount; test++ sw = (Int32 test = ; test < testCount; test++ sw = (Int32 test = ; test < testCount; test++ = (Int32 x = ; x < c_numElements; x++ (Int32 y = ; y < c_numElements; y+++= = (Int32 x = ; x < c_numElements; x++ (Int32 y = ; y < c_numElements; y+++= = (Int32* pi = (Int32 x = ; x < c_numElements; x++= x * (Int32 y = ; y < c_numElements; y+++= pi[baseOfDim +
最後請注意,不安全和安全二維數組訪問技術的速度大致相同。但是,考慮到它訪問是單個二維數組(產生一次內存分配),二不像交錯數組那樣需要許多次內存分配。所以它的速度是所有技術中最快的。
如果性能是首要目標,請避免在堆上分配托管的數組對象。相反,應該在線程棧上分配數組,這是通過C#的 stackalloc語句來完成的。stackalloc語句只能創建一維0基、由值類型元素構成的數組,而且值類型絕對不能包 含任何引用類型的字段。當然,在棧上分配的內存(數組)會在方法返回時自動釋放。
以下代碼顯示如何使用C#的stackalloc語句:
Int32 width = * pc = Char[width]; = ; (Int32 index = ; index < width; index++- index - ] =< s.Length) ? s[index] : Console.WriteLine( String(pc, Int32 widthInBytes = = widthInBytes / = ; (Int32 index = ; index < width; index++- index - ] =< s.Length) ? s[index] : Console.WriteLine( String(ca.Characters, Char Characters[
1)類型必須是結構(值類型);不能在類(引用類型)中嵌入數組。
2)字段或其定義結構必須用unsafe關鍵字標記
3)數組字段必須使用fixed關鍵字標記
4)數組必須是一維0基數組。
5)數組的元素類型必須是一下類型之一:Boolean,Char,SByte,Byte,Int16,Int32,UInt16,UInt32,Int64,UInt64,Single或Double。
內聯(內嵌)數組常用於和非托管代碼進行互操作,而且非托管數據結構也有一個內聯數組。不過,也可用於其他情況。