最近在學習.NET Framework 高級編程這本書,感覺挺有意思的,於是根據自己的理解,做了筆記,總結下內容。本文筆記主要是從.NET類型,泛型,集合這三個方面進行描述。
本文內容:
1. 類型
2. 泛型
3. 集合
1.類型
類型是對程序要處理的數據對象的分類。不同的數據對象占用存儲空間不同,操作處理方法不同,所以必須分門別類;
(1)C#的主要類型有:
基本類型:數值、字符、邏輯型、字符串、對象(object)
系統或自定義的類型:結構、枚舉、類等等。
上述這些類型,所占的內存空間不同。有的類型占用內存空間是確定的,有的不確定。
根據它們被分配存儲空間方式的不同,可以分為:值類型/引用類型兩大類。
上述的類型中,你是否又能區分它們屬於哪一類呢?
基本數據類型大部分是值類型,除了object, string屬於引用類型;類是引用類型;結構和枚舉是值類型。
(2)內存的使用分別:棧(stack), 堆(heap) 靜態區(static)。
值類型直接分配在棧上;
引用類型包括兩部分:對象的引用(地址)在棧(stack)上,對象本身在堆(heap)上。
靜態區(static)存放的是與對象的實例無關的部分。即當程序一裝入內存,就要分配好。
如:入口方法,構造方法,常量,其他用static修飾的成員。
(3)舉例:
例一:
int x, y=0;
x = y;
y = 9;
例二:設有類Myobj定義為:
class Myobj
{
public int age;
public string name;
}
Myobj a, b;
a = new Myobj();
a.age = 28; a.name = "puppy";
b = a;
a.age = 18; a.name = "doggy";
Console.WriteLine(b.age);
Console.WriteLine(b.name);
例三:
string x, y="string1";
x = y;
y = "string2";
Console.WriteLine(x);
第一個例子輸出是0。當y=9時,實際上是在棧中重新分配一個內存地址。
第二個例子輸出是18和doggy。b=a時,a和b同時指向一個地址空間
第三個例子輸出是string1。string是一個特殊的引用類型。 string實例在內存中不可修改。當需要修改時,總是創建一個新的實例,並將變量指向新的實例。
(在程序中頻繁修改字符串變量,會產生大量內存垃圾。這是我們要使用StringBuilder類的原因)
(4)裝箱和拆箱
裝箱:將值類型轉換為引用類型,按照自己的理解來說在堆中生成一個新的對象,棧中原本的地址指向該對象。
拆箱:將引用類型轉換為值類型,大致同上,過程相反。
int x=1;
Object y=x;//裝箱
int z=(int)y;/拆箱
2.泛型
問題的緣起:
請看一個Stack的程序。Stack是一種數據結構。可以存放許多元素,遵循后進先出的原則。
舉例:(Stack)
class StackOfInt {
private int[] m_ItemArray;
private int m_Index = 0;
public const int Max_Size = 100;
public StackOfInt () { m_ItemArray = new int[Max_Size]; }
public int Pop() {
if (m_Index == 0)
throw new System.InvalidOperationException("Can't
Pop an empty stack.");
return m_ItemArray[--m_Index];
}
public void Push(int item){
if (m_Index == Max_Size)
throw new System.InvalidOperationException("Can't
push an item on a full stack.");
m_ItemArray[m_Index++] = item;
}
}
缺點:這樣的棧用object類型作為元素類型,可以供所有類型使用
但在壓入元素時,要裝箱;取用元素時要拆箱,代價很大,執行效率低;
類型不安全。轉換類型時容易出錯。
解決方案:如果我們可以在Stack類的定義中,提供一個類作參數,則可以簡化此問題的解決。泛型的定義:在定義一個類型時,使用另一個或幾個類型為參數,類型參數用<>圍住,放在所定義的類型名后面。
在使用帶有類型參數的泛型類型時,同時要給定參數類型的具體類型。
舉例:定義泛型Stack類
class Stack<T> {
private T[] m_ItemArray;
private int m_Index = 0;
public const int Max_Size = 100;
public Stack() { m_ItemArray = new T[Max_Size]; }
public T Pop() {
if (m_Index == 0)
throw new System.InvalidOperationException("Can't
Pop an empty stack.");
return m_ItemArray[--m_Index];
}
public void Push(T item){
if (m_Index == Max_Size)
throw new System.InvalidOperationException("Can't
push an item on a full stack.");
m_ItemArray[m_Index++] = item;
}
}
泛型的優點:
讓代碼更具有通用性,更簡潔;
強類型,類型安全,不用擔心類型轉換錯;
不用浪費很多裝箱、拆箱的時間。
3.集合
集合類型是其對象中包含多個其他對象的特殊類型。
數組是最常用的集合類型。
要進一步理解的是,當我們在程序中申明一個數組時,實際上是指示CLR在執行期間幫我們創建和管理一個集合類型;
該集合類型的基類為System.Array
所以,我們可以對數組使用一些固有的屬性和方法,如Length,Rank,Copy,Clone。他們來自Array類;還可以對數組用foreach來遍歷。這是因為Array已經實現IEnumerable 接口。
由此我們可以牢記,數組是引用類型。可以用數組作為方法的參數,不使用ref也可以從方法中傳出修改的結果
舉例:
using System.Collection.Generic;
public class Program {
public static void Main() {
List<int> list = new List<int>(3);
for (int i=0; i<8; i++) {
list.Add(i);
System.Console.WriteLine(“Count: {0} Capacity: {1}”,
list.Count, list.Capacity);
}
list.TrimExcess();
System.Console.WriteLine(“Count: {0} Capacity: {1}”,
list.Count, list.Capacity );
}
}