陣列,一種資料型別(在絕大數語言中不是基本資料型別)且為參照型別,在記憶體中以連續的記憶體單元進行分配,所以其大小在建立物件後為定值,不可更改。
對於兩種不同資料型別而言,其記憶體分配方式是不同的。值型別直接在棧(C#中稱為堆疊Stack)上分配,將其儲存的內容直接存放到棧中;參照型別則是將指向範例物件的地址存在棧中,對該地址進行解析後獲得一個在堆(此處,C#中稱為託管堆)中的位置,這個位置儲存著真正的內容。
以上時兩種基本初始化方式。在初始化交錯陣列時,第一個索引運運算元表示長度,第二個索引運運算元表示維度,如一維[],二維[,]等。
元素的存取與之前提到的方法類似,通過單個索引運運算元存取
第一個索引運運算元表示arr中的物件,第二個索引運運算元表示對應物件中的元素。
【注:本節提到的原始碼及內容均在.NET 5的基礎上論述】
通過反編譯發現,該方法存在的16個過載最後都會呼叫Line 2125的方法。
【以下內容為原始碼分析】
(1) Nullable<T> 表示可被分配為null的值型別,[Nullable(type)]表示被修飾的資料結構可以儲存type型別的元素,沒有值則儲存null;
(2) keys 表示目標陣列,即待排序的陣列;
(3) items 表示另一個陣列(預設為null),其內部的每一個元素與keys中每個關鍵字對應;常用於兩個陣列的關聯排序,預設將keys中的元素按索引順序和items中的元素一一對應,在排序時以keys中的元素為比較物件進行排序,在對keys中的元素進行位置移動時會連帶對應items中的元素一起移動,類似於鍵值對。
(4) index:排序起始索引(預設為0);
(5) length:排序長度(預設為arr.Length);
(6) compare:比較器物件(預設為升序)。
(1) keys.Length - (index - lowerBound) < length 表示 從index開始的要排序的元素長度 小於 標稱長度length,即要排序的元素長度不夠;
(2) items != null && index – lowerBound > items.Length – length 表示 index之前的不參與排序元素長度 已經超過 items的長度,使得keys中要排序的部分沒有可對應的items元素。
(1) as 關鍵字判斷並進行轉換。若無法轉換且不發生錯誤,則返回null;否則返回轉換後得到物件;
(2) Line 2161:array != null 表示該陣列成功轉換為object型別;
(3) Line 2164:表示items為空 或 items成功轉換;
【注:(4)(5)為本人結合資料推斷得出,有待證實】
(4) as成功轉換的前提是,當需要轉化物件的型別屬於轉換目標型別 或者 屬於轉換目標型別的派生型別時,轉換操作才能成功,而且並不產生新的物件。而陣列型別在System.Array中,並不屬於System.Object,所以自帶的陣列型別無法利用as轉換為object型別。
因此,可推斷:所有基本資料型別均不能以上述方式成功轉換,因為基本資料型別屬於System.ValueType;而自定義的物件資料型別可以成功轉換,因為自定義型別屬於Sytem.Object。
(5) 如果全都轉換成功,則需要利用物件的排序方式進行排序,不能使用基本資料型別的方式進行排序。(體現在Line 2166與Line 2215)。
之後將keys轉換為Span列表,Span型別可以表示任意記憶體的相鄰區域,以此達到部分排序的目的;Span中的Sort方法位於MemoryExtensions類中。
(1) 內部的Default物件會根據是否實現了介面IComparable<T>來建立不同的 ArraySortHelper;
(2) Type.IsAssignbleFrom(Type c)方法判讀指定型別c的範例是否能分配給當前型別Type的變數;即判斷c是否為Type型別或其派生型別。
【至此,陣列開始正式進入排序階段】
(插排、堆排程式碼如下):
其中的DownHeap是建立頂堆的過程,預設為小頂堆。
1. .NET對陣列進行排序時,有較長的「前搖」,需要判斷、轉換等相關操作;
2. .NET中對陣列的排序方法不是單一的,而是綜合許多排序方法,在不同條件下選擇不同的方法,以達到最優的解法。
一個集合的淺度拷貝意味著只拷貝集合中的元素,不管他們是參照型別或者是值型別,不拷貝參照所指的物件。即,新集合中的參照和原始集合中的參照所指的物件是同一個物件。與此形成對比的是深度拷貝,不僅拷貝集合中的元素,而且還拷貝元素直接或者間接參照的所有東西。即,新集合中的參照和原始集合中的參照所指的物件是不同的。
【注:下方內容為本人結合資料推斷得出,有待證實】
將傳入的物件的Flags屬性與2^24做且運算 和 (uint)0無符號整數0進行比較。之後按照不同的情況進行資料填充,完成後返回型別為object的物件。
接下來的過程與Clone方法基本一致。
該部分主要功能是丟擲異常,因為在實際使用中,無法呼叫到包含bool reliable引數的這個方法。
可以發現,其原理和Copy方法、Clone方法基本一致。
1. 三種方法均可以將一個陣列的內容,放到另一個陣列上。
2. Clone方法具有返回值,為Object型別,在克隆後直接賦值給目標陣列,因此不需要目標陣列範例化。
2. Copy與CopyTo方法沒有返回值,是通過直接在目標陣列上填充,以完成複製,因此目標陣列必須範例化且目標陣列必須和源陣列型別一致。
4. Copy為靜態方法,可通過Array類名直接呼叫;Clone與CopyTo方法為非靜態方法,故需要範例化的一個陣列來呼叫。
5. Clone方法使淺層拷貝,Copy與CopyTo是深層拷貝。
1. 本文從原始碼的角度,對陣列、陣列遍歷以及常見方法進行了分析與論述。更多關於陣列的內容可參閱(.NET API 瀏覽器 | Microsoft Docs)。
2. 對於記憶體分配,陣列屬於參照型別,其值儲存在堆中,但參照的物件儲存在棧中(是一個記憶體地址),通過該參照物件找到並存取堆中的值。
3. 對於迭代器存取,所以可使用迭代器存取的資料型別均必須可以建立或返回一個IEnumerator<T>的物件,供迭代器使用。
4. 對於Array.Sort()方法,其內部不是單一的排序方式,而是在不同情況下使用不同的的排序方式,以達到最佳效率。
5. 對於三種複製方法,Clone為淺層拷貝,拷貝後,新陣列與源陣列參照地址相同;Copy與CopyTo為深層拷貝,拷貝後,新陣列與源陣列參照地址不同,是一個完全新的物件。
【感謝您可以抽出時間閱讀到這裡,因個人水平有限,可能存在錯誤,望各位大佬指正,留下寶貴意見,謝謝!】