一鼓蜒、數(shù)組
數(shù)組是一組使用數(shù)字索引的對象坝橡,這些對象屬于同一種類型泻帮。雖然C#為創(chuàng)建數(shù)組提供了直接的語言支持,但通用類型系統(tǒng)讓您能夠創(chuàng)建System.Array计寇。因此锣杂,C#數(shù)組屬于引用類型,對于指向數(shù)組的引用番宁,將從托管堆給它分配內(nèi)存元莫。然而,對于數(shù)組元素(數(shù)組包含的各項數(shù)據(jù))蝶押,將根據(jù)其類型分配內(nèi)存踱蠢。
在最簡單的情況下,數(shù)組變量的聲明類似于:
type[] identifer;
ps:C#數(shù)組不同于C語言數(shù)組棋电,因為它們實際上是System.Array對象茎截。因此,C#數(shù)組通過屬性和方法提供了只有類才有的靈活性和威力赶盔,但使用的語法與C語言數(shù)組一樣簡單企锌。
type指定了數(shù)組將包含的每個元素的類型。由于只聲明了類型一次于未,因此數(shù)組中所有元素的類型都必須相同撕攒。方括號為索引運算符(index operator),它告訴編譯器您要聲明一個指定類型的數(shù)組與常規(guī)變量聲明相比烘浦,數(shù)組聲明的唯一不同之處在于包含方括號抖坪。不同于其他語言(如C),數(shù)組的大小是在實例化而不是聲明時指定的闷叉。
要創(chuàng)建一個可包含5個int值的數(shù)組擦俐,可編寫類似于下面的代碼:
int[] array = new int[5];
ps:數(shù)組大小
在 C#中,數(shù)組大小指的是包含在各維中的元素總數(shù)握侧,而不是數(shù)組的最大索引(upper bound)捌肴,這可以通過屬性Length獲悉。
在 C#中藕咏,數(shù)組索引從 0 開始,因此數(shù)組中第一個元素的位置為 0秽五。下面的語句聲明了一個包含5個元素的數(shù)組孽查,索引為0~4:
int[] array = new int[5];
該數(shù)組的長度為5,但最大索引為4坦喘。
這種數(shù)組聲明創(chuàng)建一個一維的矩形數(shù)組盲再。在矩形數(shù)組中西设,每行的長度必須相同。這種限制導致數(shù)組的形狀為矩形答朋,矩形數(shù)組因此得名贷揽。要聲明多維矩形數(shù)組,可在方括號內(nèi)使用逗號指定維數(shù)(稱之為秩[rank])梦碗。最常見的多維數(shù)組是二維數(shù)組禽绪,可將其視為由行和列組成。數(shù)組最多不能超過32維洪规。
除矩形多維數(shù)組外印屁,C#還支持交錯數(shù)組。由于交錯數(shù)組的每個元素又是一個數(shù)組斩例,因此不同于矩形數(shù)組雄人,交錯數(shù)組每行的長度可以不同。
ps:交錯矩形數(shù)組
在下面的代碼中竹习,創(chuàng)建了一個二維數(shù)組和一個一維數(shù)組(j)派继。其中前者包含6個元素(共3行毙替,每行2個元素);而后者的每個元素都是一維數(shù)組旗吁,分別包含一個、兩個和3個元素:
int[,] a = {
{10,20},
{30,40},
{50,60}
};
int[][] j = {
new[] {10},
new[] {20,30},
new[] {40,50,60}
};
int[,][] j2;
實際上是一個二維數(shù)組正罢,而它的每個元素都是一個一維數(shù)組阵漏。要初始化這樣的數(shù)組,可編寫下面這樣的代碼:
j2 = new int[3,2][];
因此翻具,使用泛型集合幾乎總是更好的選擇履怯。List<int[,]>顯然表示一個二維數(shù)組列表,而List<int>[,]顯然是一個二維數(shù)組裆泳,其元素為List<int>叹洲。
類型系統(tǒng)要求所有變量在使用前都必須初始化,并為每種數(shù)據(jù)類型提供了默認值工禾。數(shù)組也不例外运提。對于包含數(shù)值元素的數(shù)組,每個元素的默認初始值都為0闻葵;對于包含引用類型(包括string)民泵,每個元素的默認初始值都為null。由于交錯數(shù)組的元素是數(shù)組槽畔,因此這些元素的默認初始值也為null栈妆。
1.1 數(shù)組索引
要讓數(shù)組發(fā)揮作用,必須訪問其元素,這是通過將所需元素的數(shù)字位置放在索引運算符中實現(xiàn)的鳞尔。要訪問多維數(shù)組或交錯數(shù)組的元素嬉橙,需要提供多個索引位置。
1.2 數(shù)組初始化
使用運算符new可創(chuàng)建一個數(shù)組寥假,并將其元素初始化為默認值市框。在這種情況下,必須指定秩(rank)糕韧,讓編譯器能夠知道數(shù)組的大小枫振。以這種方式聲明數(shù)組時,必須編寫額外的代碼兔沃。
舉個栗子
class Program
{
static void Main ()
{
int[] array = new int[5];
for (int i = 0; i < array.Length; i++)
{
array [i] = i * 2;
}
}
}
C#提供了一種簡捷方法蒋得,讓聲明和初始化數(shù)組時無需重復指出數(shù)組的類型。這種簡捷方法稱為數(shù)組初始值設(shè)定項乒疏,可在聲明數(shù)組型局部變量和字段時使用额衙,還可放在數(shù)組構(gòu)造函數(shù)調(diào)用的后面。數(shù)組初始值設(shè)定項是一系列變量初始值設(shè)定項怕吴,它們用逗號分開窍侧,并用大括號括起。每個變量初始值設(shè)定項都是表達式转绷;如果初始化的是多維數(shù)組伟件,則為嵌套的數(shù)組初始值設(shè)定項。
用于一維數(shù)組時议经,數(shù)組初始值設(shè)定項必須包含一系列表達式斧账,其類型與數(shù)組元素的類型兼容。這些表達式從索引0開始初始化元素煞肾,而表達式數(shù)量決定創(chuàng)建的數(shù)組的長度咧织。
改寫如下
class Program
{
static void Main ()
{
int[] array = { 0, 2, 4, 6, 8 };
}
}
數(shù)組初始值設(shè)定項對一維數(shù)組來說很有用;對多維數(shù)組來說籍救,其威力將更強大习绢,且需要使用嵌套的數(shù)組初始值設(shè)定項。在這種情況下蝙昙,數(shù)組初始值設(shè)定項的嵌套深度必須與數(shù)組的維數(shù)相等闪萄。其中,最左邊的維數(shù)對應于最外面的嵌套級別奇颠,最右邊的維數(shù)對應于最里面的嵌套級別败去。
如下兩個聲明是等價的
int[,] array = { {0, 1}, {2, 3}, {4, 5}, {6, 7}, {8, 9} };
int[,] array = new internal[5,2];
array[0,0] =0; array[0,1] = 1;
array[1,0] =2; array[1,1] = 3;
array[2,0] =4; array[2,1] = 5;
array[3,0] =6; array[3,1] = 7;
array[4,0] =8; array[4,1] = 9;
1.3 System.Array類
System.Array 類是數(shù)組的基類,但只有運行時和編譯器可顯式地從它派生出其他類烈拒。雖然存在這種限制为迈,但它提供了25個不同的靜態(tài)方法供大家使用三椿。這些方法主要用于一維數(shù)組,但鑒于一維數(shù)組是最常見的葫辐,因此這種限制通常影響不大。
下表是一些較常用的方法
方法 | 描述 |
---|---|
BinarySearch | 使用二分法搜索算法在一維排序數(shù)組中搜索指定的值 |
Clear | 根據(jù)元素類型將指定范圍內(nèi)的元素設(shè)置為零伴郁、false或null |
Exists | 確定指定數(shù)組是否包含符合指定條件的元素 |
Find | 在整個數(shù)組中搜索符合指定條件的元素耿战,并返回第一個符合條件的元素 |
FindAll | 返回符合指定條件的所有元素 |
ForEach | 對數(shù)組中的每個元素執(zhí)行指定的操作 |
Resize | 將數(shù)組調(diào)整到指定大小。如果數(shù)組大小沒變焊傅,那么什么也不會發(fā)生剂陡;如果數(shù)組變大了,就創(chuàng)建一個新數(shù)組狐胎,并將舊數(shù)組中的元素復制到新數(shù)組中 |
Sort | 對指定數(shù)組中的元素進行排序 |
TrueForAll | 確定數(shù)組中的每個元素是否都符合指定的條件 |
二鸭栖、索引器
通過使用索引運算符,訪問數(shù)組非常簡單握巢。雖然不可能重寫索引運算符晕鹊,但在自己創(chuàng)建的類中,可以提供一個索引器暴浦,這樣就可以像訪問數(shù)組那樣訪問對象的數(shù)據(jù)溅话。
索引器的聲明方法與屬性類似,但存在一些重要的差別歌焦。其中飞几,最重要的差別如下:
索引器是用簽名而不是名稱標識的。
索引器只能是實例成員
索引器的簽名指的是其形參的數(shù)量和類型独撇。由于索引器是用簽名標識的屑墨,因此只要它們的簽名在當前類中是唯一的,就可以重載索引器纷铣。
要聲明索引器卵史,可使用如下語法:
type this [type parameter]
{
get;
set;
}
對于索引器,可使用修飾符new关炼、virtual程腹、sealed、override儒拂、abstract以及4個訪問修飾符的有效組合寸潦。請記住,由于索引器必須是實例成員社痛,因此不能對其使用修飾符static见转。索引器的形參列表必須至少包含一個參數(shù),但是也可包含多個用逗號分開的參數(shù)蒜哀。這類似于方法的形參列表斩箫。索引器的類型決定了get訪問器返回的對象類型。
索引器總是應該提供get訪問器(雖然這并非必須的),但可以不提供set訪問器乘客。只提供了get訪問器的索引器是只讀的狐血,因為它們不允許賦值。
三易核、泛型集合
在C#內(nèi)置的數(shù)據(jù)結(jié)構(gòu)中匈织,只有數(shù)組支持一系列對象,但作為補充牡直,基類庫提供了大量集合以及與集合相關(guān)的類型缀匕,這些類型更靈活,能夠根據(jù)要使用的數(shù)據(jù)類型派生出自定義集合碰逸。
這些類型分為類和接口乡小,而它們又進一步分為非泛型集合和泛型集合。非泛型集合不是類型安全的饵史,因為它們只能用于 object類型满钟,這旨在與舊版本的.NET Framework兼容。泛型集合更好约急,因為它們是類型安全的零远,且性能優(yōu)于非泛型集合。泛型集合的數(shù)量幾乎是非泛型集合的兩倍厌蔽。
3.1 列表
在所有的集合類型中牵辣,List<T>與數(shù)組最接近,也是用得最多的集合奴饮。與數(shù)組一樣纬向,列表也是一系列使用數(shù)字索引的對象;不同的是戴卜,可在需要時動態(tài)調(diào)整List<T>的大小逾条。
列表的默認容量為16個元素,當您添加第17個元素時投剥,列表的大小將自動翻倍师脂。如果預先知道列表大概包含多少個元素,可在添加第一個元素前使用構(gòu)造函數(shù)之一或Capacity屬性設(shè)置初始容量江锨。
如下列出了List<T>的一些常用屬性和方法吃警。如果將其與System.Array的常用靜態(tài)方法進行比較,將發(fā)現(xiàn)有很多相似之處
成員 | 描述 |
---|---|
Capacity | 獲取或設(shè)置列表在不調(diào)整大小的情況下可包含的元素總數(shù) |
Count | 獲取列表實際包含的元素數(shù) |
Add | 將一個對象添加到列表末尾 |
AddRange | 將一組對象添加到列表末尾 |
BinarySearch | 使用二分法在排序列表中搜索特定的值 |
Clear | 刪除列表中所有的元素 |
Contains | 確定列表是否包含特定元素 |
Exists | 確定列表是否包含滿足指定條件的元素 |
Find | 在整個列表中搜索滿足指定條件的元素啄育,并返回第一個滿足條件的元素 |
FindAll | 獲取滿足指定條件的所有元素 |
ForEach | 對列表中的每個元素執(zhí)行指定的操作 |
Sort | 對列表中的元素進行排序 |
TrimExcess | 將容量設(shè)置為列表實際包含的元素數(shù) |
TrueForAll | 確定是否列表中的每個元素都滿足指定的條件 |
一個與List<T>相關(guān)的泛型集合是LinkedList<T>酌心,這是一種通用的雙向鏈表,在通常將元素添加到列表的特定位置且總是順序訪問元素時挑豌,雙向鏈表可能是更好的選擇安券。
3.2 Collection<T>
雖然 List<T>功能強大墩崩,但是它沒有虛成員,也不能禁止修改列表侯勉。由于沒有虛成員鹦筹,因此難以對其進行擴展,這使其在用作基類方面的用途有限址貌。
要創(chuàng)建自己的集合盛龄,且能夠像數(shù)組那樣使用數(shù)字索引訪問它,可從Collection<T>派生芳誓;也可直接使用Collection<T>類,即創(chuàng)建一個Collection<T>實例啊鸭,并指定要包含的對象類型锹淌。
如下列出了常用的Collection<T>的成員
成員 | 描述 |
---|---|
Count | 獲取集合中實際包含的元素數(shù) |
Add | 將一個對象加入到集合末尾 |
Clear | 刪除集合中所有的元素 |
Contains | 判斷集合是否包含指定的元素 |
從Collection<T>派生時,可重寫它提供的多個受保護的虛方法赠制,以自定義新類的行為赂摆。
如下是Collection<T>受保護的虛方法
成員名 | 描述 |
---|---|
ClearItems | 刪除集合中的所有元素。通過重寫該方法钟些,可改變Clear方法的行為 |
InsertItem | 將元素插入到集合的指定索引處 |
RemoveItem | 刪除指定索引處的元素 |
SetItem | 替換指定索引處的元素 |
一個與 Collection<T>相關(guān)的集合是 ReadOnlyCollection<T>烟号,可像 Collection<T>那樣直接使用它,也可使用它來派生只讀集合政恍。另外汪拥,還可使用List<T>實例創(chuàng)建只讀集合。
如下是常用的ReadOnlyCollection<T>成員
成員名 | 描述 |
---|---|
Count | 獲取集合實際包含的元素數(shù) |
Contains | 判斷集合是否包含指定的元素 |
IndexOf | 搜索指定的元素篙耗,并返回找到的第一個元素的索引 |
ps:ReadOnlyCollection<T>
可將ReadOnlyCollection<T>視為現(xiàn)有可變集合的封裝迫筑,如果您試圖修改它,就將引發(fā)異常宗弯。底層集合仍是可變的脯燃。
3.3 字典
創(chuàng)建通用集合時,List<T>和Collection<T>很有用蒙保,但有時需要這樣的集合辕棚,即將一組鍵映射到一組值,且不能有重復的鍵邓厕。
Dictionary<TKey, TValue>類提供了這樣的映射逝嚎,能夠使用鍵而不是數(shù)字索引進行訪問。要在Dictionary<TKey, TValue>實例中添加元素邑狸,必須提供鍵和值懈糯。其中,鍵必須是唯一的单雾,且不為null赚哗,但如果TValue為引用類型她紫,那么值可以為null。
如下列出了Dictionary<TKey, TValue>的常用成員
成員名 | 描述 |
---|---|
Count | 獲取字典包含的鍵/值對數(shù) |
Keys | 返回一個集合屿储,其中包含字典中所有的鍵 |
Values | 返回一個集合贿讹,其中包含字典中所有的值 |
Add | 將指定的鍵和值加入到字典中 |
Clear | 刪除字典中所有的鍵和值 |
ContainsKey | 確定字典是否包含所有的鍵 |
ContainsValue | 確定字典是否包含所有的值 |
Remove | 將指定鍵對應的元素從字典中刪除 |
不同于 List<T>和 Collection<T>,遍歷字典中的元素時够掠,將返回 KeyValuePair<TKey, TValue>結(jié)構(gòu)民褂,它表示鍵和相關(guān)聯(lián)的值。因此疯潭,在foreach語句中遍歷字典的元素時赊堪,關(guān)鍵字var很有用。
通常竖哩,字典不對其包含的元素進行排序哭廉,遍歷時將以隨機順序返回元素。List<T>提供了一個Sort方法相叁,可用于對列表中的元素進行排序遵绰,但字典沒有這樣的方法。希望添加或刪除元素時增淹,字典對元素進行排序椿访,可以有兩種選擇:使用 SortedList<TKey, TValue>或SortedDictionary<TKey, TValue>。這兩個類相似虑润,在檢索元素方面的性能相同成玫,但占用的內(nèi)存不同,插入和刪除元素的性能也不同端辱。
- SortedList<TKey, TValue>使用的內(nèi)存更少
- SortedDictionary<TKey,TValue>使用的內(nèi)存更少
- 使用經(jīng)過排序的數(shù)據(jù)一次性填充時梁剔,SortedDictionary<TKey,TValue>的速度更快
- 檢索鍵和值時,SortedDictionary<TKey,TValue>的效率更高
如下所示列出了SortedList<TKey, TValue>和SortedDictionary<TKey, TValue>的常用成員
成員名 | 描述 |
---|---|
Capacity | 只有SortedList<TKey,TValue>支持舞蔽。獲取或設(shè)置列表可包含的元素數(shù) |
Count | 獲取列表包含的鍵/值對數(shù) |
Add | 將指定的鍵和值加入到列表中 |
Clear | 刪除列表中所有的鍵和值 |
ContainsKey | 確定列表是否包含指定的鍵 |
ContainsValue | 確定列表是否包含指定的值 |
Remove | 將指定鍵對應的元素從列表中刪除 |
TrimExcess | 如果實際包含的元素小于當前容量的90%荣病,將容量設(shè)置為實際包含的元素數(shù) |
TryGetValue | 獲取與指定鍵相關(guān)聯(lián)的值 |
3.4 集
在數(shù)學中,集(set)是一個不包含重復元素的集合渗柿,以隨機順序存取个盆。除標準的插入和刪除操作外,集還支持超集朵栖、子集颊亮、交集和并集操作。
在.NET中陨溅,集是通過HashSet<T>和SortedSet<T>類實現(xiàn)的终惑。HashSet<T>是數(shù)學意義上的集,而 SortedSet<T>在插入和刪除元素時保持元素按特定順序排列门扇,并且不會影響性能雹有。這兩個類都不允許元素重復偿渡,它們的公有接口幾乎相同
如下所示列出了HashSet<T>和SortedSet<T>的常用成員
成員名 | 描述 |
---|---|
Count | 獲取集包含的元素數(shù) |
Max | 獲取集中最大的值(只適用于SortedSet<T>) |
Min | 獲取集中最小的值(只適用于SortedSet<T>) |
Add | 將指定元素加入集中 |
Clear | 刪除集中所有的元素 |
Contains | 判斷集是否包含指定的元素 |
ExceptWith | 將指定集合中的所有元素從當前集中刪除 |
IntersectWith | 修改當前集,使其只包含當前集和指定集合中都有的元素 |
IsProperSubsetOf | 判斷當前集是否是指定集合的真子集 |
IsProperSupersetOf | 判斷當前集是否是指定集合的真超集 |
Overlaps | 判斷當前集與指定集合是否有相同的元素 |
Remove | 將指定元素從集中刪除 |
RemoveWhere | 將符合指定條件的所有元素都從集中刪除 |
Reverse | 返回一個枚舉器霸奕,它以倒序遍歷集(只適用于Sorted<T>) |
SetEquals | 判斷當前集與指定集合包含的元素是否相同 |
SymmetricExceptWith | 修改當前集溜宽,使其只包含這樣的元素,即要么出現(xiàn)在當前集中质帅,要么出現(xiàn)在指定集合中适揉,但不同時出現(xiàn)在這兩者中 |
TrimExcess | 設(shè)置當前集的容量,使其等于實際包含的元素數(shù)(向上舍入到最接近的值) |
UnionWith | 修改當前集煤惩,使其包含出現(xiàn)在當前集或指定集合中的所有元素 |
ps:為何是HashSet<T>嫉嘀?
不同于大多數(shù)其他的泛型集合,HashSet<T>的名稱基于其實現(xiàn)細節(jié)魄揉,而不是其用途吃沪。這樣做的原因是,Set是Visual Basic保留字什猖,要使用它,只能進行轉(zhuǎn)義:
Dim s as [Set] of Int
為避免使用這種語法红淡,設(shè)計.NET Framework的人員選擇了一個不與任何保留字發(fā)生沖突的名稱不狮。
3.5 堆棧和隊列
堆棧和隊列相對簡單,它們分別表示后進先出(LIFO)和先進先出(FIFO)集合在旱。雖然它們是簡單集合摇零,但也很有用。對存儲按收到的順序依次處理的數(shù)據(jù)來說桶蝎,隊列很有用驻仅;而對諸如語句分析等操作來說,堆棧很有用登渣。一般而言噪服,當操作應限于列表開頭或末尾時,可使用隊列和堆棧胜茧。
Stack<T>類以數(shù)組的方式實現(xiàn)了堆棧粘优,其操作總是發(fā)送在數(shù)組末尾,且可包含重復的元素和null元素呻顽。Stack<T>提供了簡單的公有接口雹顺,如下所示
成員名 | 描述 |
---|---|
Count | 獲取堆棧包含的元素數(shù) |
Clear | 刪除堆棧中所有的元素 |
Contains | 判斷堆棧是否包含指定的元素 |
Peek | 返回棧頂元素,但不將其刪除 |
Pop | 返回棧頂元素并將其刪除 |
Push | 將一個元素插入棧頂 |
TrimExcess | 如果堆棧包含的元素數(shù)小于當前容量的90%廊遍,就將容量設(shè)置為堆棧包含的元素數(shù) |
Queue<T>以數(shù)組方式實現(xiàn)了隊列嬉愧,其插入操作總是在數(shù)組的一端進行,而刪除操作總是在另一端進行喉前。Queue<T>可包含重復的元素和 null 元素没酣。Queue<T>提供了一個簡單的公有接口王财,其常用成員如下所示
成員名 | 描述 |
---|---|
Count | 獲取隊列包含的元素數(shù) |
Clear | 刪除隊列中所有的元素 |
Contains | 判斷隊列是否包含指定的元素 |
Dequeue | 返回隊列開頭的元素并將其刪除 |
Enqueue | 將一個元素加入隊列末尾 |
Peek | 返回隊列開頭的元素,但不刪除它 |
TrimExcess | 如果隊列實際包含的元素數(shù)小于當前容量的90%四康,就將容量設(shè)置為隊列實際包含的元素數(shù) |
四搪搏、集合初始值設(shè)定項
數(shù)組提供了數(shù)組初始化語法,對象提供了對象初始化語法闪金,同樣集合提供了集合初始化語法:集合初始值設(shè)定項疯溺。
集合初始值設(shè)定項能夠指定一個或多個元素,以初始化實現(xiàn)了IEnumerable的集合哎垦。通過使用集合初始值設(shè)定項囱嫩,可以省略多次調(diào)用Add方法的代碼,而讓編譯器為您添加這些調(diào)用代碼漏设。元素初始值設(shè)定項可以是值墨闲、表達式或?qū)ο蟪跏贾翟O(shè)定項。
ps:集合初始值設(shè)定項只能用于有Add方法的集合
集合初始值設(shè)定項只能用于有 Add 方法的集合郑口,這意味著不能將其用于Stack<T>和Queue<T>等集合鸳碧。
集合初始值設(shè)定項的語法與數(shù)組初始值設(shè)定項類似。您仍必須調(diào)用new運算符犬性,但接著可使用集合初始化語法來填充集合瞻离。
如下示例使用List<int>和集合初始值設(shè)定項
class Program
{
static void Main()
{
List<int> list = new List<int> (){0, 2, 4, 6, 8};
}
}
集合初始值設(shè)定項可以更復雜,您可以將對象初始值設(shè)定項用作元素初始值設(shè)定項乒裆,舉個栗子
class Program
{
static void Main()
{
List<Contact> list = new List<Contact> ()
{
new Contanct () { FirstName = "Oliver", LastName = "Kahn" },
new Contanct () { FirstName = "Roy", LastName = "Makaay" },
new Contanct () { FirstName = "Bastian", LastName = "Schweinsteiger" }
};
foreach (Contact c in list)
{
Console.WriteLine (c);
}
}
}
由于可以在集合初始值設(shè)定項中使用對象初始值設(shè)定項套利,因此集合初始值設(shè)定項可用于任何類型的集合,包括Add方法接受多個參數(shù)的字典鹤耍。
五肉迫、集合接口
集合接口分兩類,一類提供了具體的集合實現(xiàn)協(xié)定稿黄,另一類提供支持實現(xiàn)喊衫,如比較和枚舉功能。在第一類(即提供具體集合行為的)接口只有4個杆怕。
ICollection<T>:定義了用于操作泛型集合的方法和屬性格侯。
IList<T>:它擴展了 ICollection<T>,以提供用于操作這樣的泛型集合的方法和屬性财著,即其元素可通過索引進行訪問联四。
IDictionary<TKey, TValue>:它擴展了 ICollection<T>,以提供用于操作這樣的泛型集合的方法和屬性撑教,即其元素為鍵/值對朝墩,且每個元素的的鍵都必須是唯一的。
ISet<T>:它擴展了 ICollection<T>,以提供用于操作這樣的泛型集合的方法和屬性收苏,即其元素都是唯一的亿卤,并支持集運算。
第二類也只包含4個接口鹿霸,具體如下
IComparer<T>:定義了一個對兩個對象進行比較的方法排吴。這個接口用于支持方法List<T>.Sort和List<T>.BinarySearch,提供了一種自定義排序的方式懦鼠。Compare<T>類提供了這個接口的默認實現(xiàn)钻哩,通常足以滿足大部分需求。
IEnumberable<T>:它擴展了 IEnumerable肛冶,通過暴露一個枚舉器提供了對集合進行簡單迭代的支持街氢。提供這個接口旨在與非泛型集合兼容:只要泛型集合實現(xiàn)了這個接口,就可將其傳遞給需要IEnumerable對象的方法睦袖。
IEnumerator<T>:也提供了對集合進行簡單迭代的支持珊肃。
IEqualityComparer<T>:讓您能夠給類型 T提供相等的定義。EqualityComparer<T>類提供了這個接口的默認實現(xiàn)馅笙,通常足以滿足大部分需求伦乔。
六、可枚舉的對象和迭代器
如果查看IEnumerable<T>和IEnumerator<T>的定義董习,就會發(fā)現(xiàn)它們很像评矩。這些接口(及其對應的非泛型接口)遵循了迭代器模式(iterator pattern)。
這種模式讓您能夠要求實現(xiàn)了IEnumerable<T>的對象(可枚舉的對象)提供一個枚舉器(實現(xiàn)了IEnumerator<T>的對象)阱飘。有了IEnumerator<T>后,就可以每次一個元素的方式枚舉(迭代)數(shù)據(jù)虱颗。
如下示例是一條典型的foreach語句沥匈,它迭代一個列表,并顯示每個值
List<int> list = new List<int>() { 0, 2, 4, 6, 8 };
foreach(int if in list)
{
Console.WriteLine(i);
}
實際上忘渔,編譯器將把上述代碼轉(zhuǎn)換為類似于如下的代碼:
List<int> list = new List<int>() { 0, 2, 4, 6, 8 };
IEnumerator<int> iterator = ((IEnumerable<int>)list).GetEnumerator();
while (iterator.MoveNext())
{
Console.WriteLine(iterator.Current);
}
GetEnumerator方法是在接口IEnumberable<T>中定義的(實際上高帖,這是該接口定義的唯一一個成員),它只是提供了一種方式畦粮,讓可枚舉的成員能夠暴露一個枚舉器散址。正是這個枚舉器通過方法MoveNext和屬性Current(它們是在接口IEnumerator<T>和IEnumerator中定義的),讓你能夠遍歷可枚舉的對象包含的數(shù)據(jù)宣赔。
ps:為何提供兩個接口
通過將可枚舉的對象( IEnumberable<T>)同用于該對象的枚舉器(IEnumerator<T>)分開预麸,可同時對同一個可枚舉源進行多次枚舉。顯然儒将,都不希望這些迭代操作在數(shù)據(jù)中移動時相互干擾吏祸,因此需要兩個獨立的枚舉器向您提供當前的元素以及移到下一個元素的機制。
所幸的是钩蚊,所有泛型集合(以及非泛型集合)都實現(xiàn)了IEnumerable<T>贡翘、IEnumerable蹈矮、IEnumerator<T>和IEnumerator,因此您無需編寫實現(xiàn)迭代器機制的代碼就可以使用它們鸣驱。
如果希望自己創(chuàng)建的類也支持這種行為泛鸟,可以讓它們實現(xiàn)IEnumerable<T>,并編寫自己的IEnumerator<T>派生類踊东,但如果使用迭代器北滥,就不需要這樣做。
迭代器是一個方法递胧、屬性 get 訪問器或運算符碑韵,它返回同一種類型的值的有序序列。關(guān)鍵字 yield告訴編譯器缎脾,其所屬代碼塊是一個迭代器塊祝闻。yield return語句返回指定的值,而yield break語句終止迭代遗菠。對于這種迭代器方法联喘、訪問器或運算符,存在如下限制辙纬。
不能包含不安全的代碼塊
不能接受ref或out參數(shù)
yield return語句不能位于try-catch塊內(nèi)豁遭,但可位于后面跟finally塊的try塊內(nèi)
yield break語句可位于try塊或catch塊內(nèi),但不能位于finally塊內(nèi)
yield語句不能位于匿名方法內(nèi)
如下所示為一個簡單的迭代器
class Program
{
static IEnumerable<int> GetEvenNumbers()
{
yield return 0;
yield return 2;
yield return 4;
yield return 6;
yield return 8;
}
static void Main()
{
foreach (int i in GetEvenNumbers())
{
Console.WriteLine(i);
}
}
}
除返回值外贺拣,迭代器還有很多功能蓖谢。只要遵守前面指出的限制,迭代器在決定返回什么值時譬涡,其邏輯可以非常復雜闪幽。如下代碼輸出與上邊的相同,但使用的迭代器更復雜涡匀。
class Program
{
static IEnumerable<int> GetEvenNumbers()
{
for (int i = 0; i <= 9; i++)
{
if (i % 2 == 0)
{
yield return i;
}
}
}
static void Main()
{
foreach (int i in GetEvenNumbers())
{
Console.WriteLine(i);
}
}
}