程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> CLR筆記:13.數組

CLR筆記:13.數組

編輯:關於.NET

CLR支持一維/多維/交錯數組。

兩種聲明方式:

Array a;
a = new String[0, 1];
String[] s = new String[5];

注意,聲明不要給與數組長度,因為此時還不分配內存;new時要指定長度。

將數組聲明為Array和像String[]這樣帶中括號的,效果是一樣的,只是前者更靈活,當然類型不安全 ,可能會有運行期錯誤。

所有數組都隱式派生於System.Array,後者派生自System.Object,所以數組是引用類型。

值類型的數組,new時,會創建實際的對象,所有元素都初始化為0;

引用類型的數組,new時,只是創建引用,不會創建實際的對象。

CLS要求數組必須是0基數組。

每個數組都關聯了一些額外的信息,稱為開銷信息(Overhead)。

JITer只在執行一次循環之前檢查一次數組邊界,而不是每次循環都這麼做。

13.1    數組的類型轉換

對於引用類型的數組,兩個數組類型必須維數相同,並且從源元素類型到目標元素類型,必須存在一 個隱式(向父類轉)或顯示(向子類轉)的類型轉換。

對於值類型的數組,不能將其轉換為其他任何一種類型——使用Array.Copy方法替代:

            Int32[] ildim = new Int32[5];

            Object[] obldim = new Object[ildim.Length];

            Array.Copy(ildim, obldim, ildim.Length);

Array.Copy還可以再復制每個數組元素時進行必要的類型轉換:

*將值類型的元素裝箱為引用類型的元素

*將引用類型的元素拆箱為值類型的元素

*加寬CLR基元值類型,Int32到Double

*如果兩個數組的類型不兼容(從Object[]轉型為IFormattable[]),就對元素進行向下類型轉換。

數組的協變性:將數組從一種類型轉換為另一種類型,——會有性能損失,如下代碼,會在運行期報 錯:

String[] sa = new String[100];
Object[] oa = sa;
oa[5] = "Jax";
oa[3] = 1;  //運行期錯誤

如果只是需要將數組中的某些元素復制到另一個數組,可以使用System.Buffer的BlockCopy()方法, 執行速度比Array.Copy快,但是只支持基元類型,不具有轉型能力。

13.3 所有數組都隱式實現IEnumerable,ICollection,IList

對於泛型接口,因為多維數組和非0基數組的問題,System.Array並不完全實現。

只有一維0基的引用類型的數組,在創建時,會自動實現了IEnumerable<T>, ICollection<T>,IList<T>,T為這個數組的類型;還為T的所有基類型B實現了 IEnumerable<B>,ICollection<B>,IList<B>接口。

如FileStream[] fsArray,會自動實現了IEnumerable<FileStream>, ICollection<FileStream>,IList<FileStream>,同時會實現IEnumerable<Stream> ,ICollection<Stream>,IList<Stream>,IEnumerable<Object>, ICollection<Object>,IList<Object>,所以fsArray可以作為參數傳遞給以下方法:

        void M1(IList<FileStream> fsList) { }

        void M2(ICollection<Stream> sCollection) { }

        void M3(IEnumerable<Object> oEnumerable) { }

但是,對於一維0基的值類型的數組,在創建時,會自動實現了IEnumerable<T>, ICollection<T>,IList<T>,T為這個數組的類型;但是不會為其基類實現接口。如 DateTime[] dtArray;這個值類型dtArray不能傳遞給上面的M3方法。

13.4    數組的傳遞和返回

Array.Copy返回的是淺Copy。

數組最好是0長度,而不是null

13.5    非0基數組

使用Array.CreateInstance方法,可以指定數組中元素類型,數組維數,數組下界,數組每一維中元 素個數,有若干重載,如下:

            //重載1,數組長度

            Decimal[] fsArray = (Decimal[])Array.CreateInstance(typeof(Decimal), 

2);


            //重載2,數組長度用一個數組表示,指定多維數組的維數和各維上的長度4和5

            Int32[] lengths = { 4, 5 };

            Decimal[,] fsArray = (Decimal[,])Array.CreateInstance(typeof

(Decimal), lengths);


            //重載3,最後一個參數指定數組下界

            Decimal[] fsArray = (Decimal[])Array.CreateInstance(typeof(Decimal), 

2, 1);


            //重載4,最後一個數組參數,指定數組各維上的下界

            Int32[] lengths = { 4, 5 };

            Int32[] lowerBounds = { 1, 2 };

            Decimal[,] fsArray = (Decimal[,])Array.CreateInstance(typeof

(Decimal), lengths, lowerBounds);


            //重載5,創建一個三維數組,後三個參數分別指定各維的長度

            Decimal[,,] fsArray = (Decimal[,,])Array.CreateInstance(typeof

(Decimal), 2, 3, 4);

可以使用GetLowerBound(維數);與GetUpperBound(維數);獲取數組的邊界

在一位數組fsArray中,可以使用fsArray數組對象的GetValue()和SetValue()方法來操作數組,但是 比較慢。

13.6 數組訪問性能

對於1維數組,0基的typeof為String.String[];非零基為String.String[*]

對於多維數組,都會顯示String.String[,]。在運行時,CLR將多維數組視為非零基數組。

訪問1維零基數組 比 非零基1維數組 和 多維數組 速度快很多。這是因為:

1.有特殊的IL指令,用於處理1維零基數組,而不用在索引中減去偏移量

2.JIT會將索引范圍檢查代碼從循環中取出,從而只執行一次檢查。

關於for循環遍歷數組:

            Int32 a = new Int32[5];


            for (Int32 index = 0; index < a.Length; a++)

            { 

                //對a[index]進行操作

            }

Length是一個數組屬性,調用一次後,JIT會將結果存入一個臨時變量,以後每次循環迭代訪問的都是 這個變量,而不是再次計算長度,從而速度更快。

在循環前,JIT編譯器會自動生成代碼檢測有效范圍:if((Length-1) < a.GetUpperBound(0)),只 會檢測一次。

如果將Length保存在一個本地變量,在for循環時每次都會比較該變量,反而會損害性能。

以上只適用於0基數組。非零基數組中,JIT編譯器在循環中,每次都要檢查制指定索引范圍是否有效 ,此外還要從指定索引減去數組下界,從而降低了性能。

提升性能的兩個辦法:

使用交錯數組(數組的數組)來代替多維數組。

使用非安全數組代替非零基數組,在訪問數組時關閉索引邊界檢查——只適用於基元值類型。

使用非安全代碼訪問2維數組:

        //用不安全代碼訪問2維數組中所有元素

        public static unsafe void Unsafe2DimArrayAccess(Int32[,] a)

        {

            Int32 dim0LowIndex = 0; //等價於a.GetLowerBound(0)

            Int32 dim0HighIndex = a.GetUpperBound(0);


            Int32 dim1LowIndex = 0; //等價於a.GetLowerBound(1)

            Int32 dim1HighIndex = a.GetUpperBound(1);


            Int32 dim0Elements = dim0HighIndex - dim0LowIndex;


            fixed (Int32* pi = &a[0, 0])

            {

                for (Int32 x = dim0LowIndex; x <= dim0HighIndex; x++)

                {

                    Int32 baseOfDim = x * dim0Elements;


                    for (Int32 y = dim1LowIndex; y <= dim1HighIndex; 

y++)

                    {

                        Int32 element = pi[baseOfDim + y];

                    }

                }

            }

        }

13.7 非安全數組和內嵌數組

非安全數組可以訪問以下元素:

托管堆上的數組

非托管堆的數組

線程堆棧上的數組

在性能第一的前提下,避免在堆上分配數組對象,應該在線程堆棧上分配——使用stackalloc

stackalloc只適用於創建1維0基值類型數組,而且值類型中決不能包括任何引用類型。見以下方法, 將一個字符串倒置:

        public static void StackallocDemo()

        {

            unsafe

            {

                const Int32 width = 20;


                //在堆棧上分配數組

                Char* pc = stackalloc Char[width];


                String s = "Jax Bao";


                for (Int32 index = 0; index < width; index++)

                {

                    pc[width - index - 1] = (index < s.Length) ? s

[index] : ' ';

                }


                String newS = new String(pc, 0, width);


                Console.WriteLine(newS.Trim());

            }

        }

在struct中定義數組,一般來說,數組本身在struct的外部。

要使數組內嵌在struct中,要遵從:

1.struct要用unsafe標記

2.數組字段要用fixed標記

3.數組必須是1維0基的

4.數組類型必須是基元值類型

采用內嵌數組實現的方法,將一個字符串倒置:

        public static void InlineArrayDemo()
        {

            unsafe
            {
                CharArray ca;   //在堆棧上分配數組
                Int32 widthInBytes = sizeof(CharArray);
                Int32 width = widthInBytes / 2;
                String s = "Jax Bao";
                for (Int32 index = 0; index < width; index++)
                {

                    ca.Characters[width - index - 1] = (index < 

s.Length) ? s[index] : ' ';

                }
                String newS = new String(pc, 0, width);
                Console.WriteLine(newS.Trim());
            }
        }
        public unsafe struct CharArray

        { 
            public fixed Char Characters[20];

        }
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved