【記】《.net之美》之讀書筆記(二) C#中的泛型

程式猿貝塔發表於2020-10-10

前言

上一篇讀書筆記,很多小夥伴說這本書很不錯,所以趁著國慶假期,繼續我的讀書之旅,來跟隨書中作者一起溫習並掌握第二章的內容吧。

一.理解泛型

1.為什麼要使用泛型?-----通過使用泛型,可以極大地提高程式碼的重用度,同時還可以獲得強型別的支援,提升了應用程式的效能,避免了隱式的裝箱、拆箱,以及執行時的型別轉換錯誤。

2.為什麼要有泛型? -----以簡單的陣列排序為例,第一次我們可能會要求對int型陣列進行排序,然後我們很快的寫出了答案,

第二次,又要求我們對byte[]陣列進行排序,這時候我們可能會思考下,是不是要優化我們的設計思想,但是還不至於修改,我們還是快速的將int[]陣列的排序程式碼進行了複製,將方法簽名改為byte[].

第三次,又要求我們對char[]陣列進行排序,這個時候我們就不得不停下來思考了,以上兩種需求,除了方法簽名不一樣以為,其他的都一模一樣。所以我們引進了泛型的概念,將int[]、byte[]、char[]使用T[]來表示,T代表type,指代任何型別。所以我們又得到了泛型類的概念。

public class SortHelper<T> {
public void BubbleSort(T[] array){
// 方法實現體
}
}

這樣,當我們需要對int[]進行排序時,我們可以這樣宣告:

SortHelper<int> sorter = new SortHelper<int>();
int[] array = { 8, 1, 4, 7, 3 };
sorter.BubbleSort(array);

當我們需要對char[]進行排序時,我們就能這樣宣告:

SortHelper<char> sorter = new SortHelper<char>();
char [] array = { 8, 1, 4, 7, 3 };
sorter.BubbleSort(array);

再也不用重複的對程式碼進行復制,然後修改方法簽名了。

結論:通過使用泛型,極大的減少了重複程式碼,使程式更加清爽。泛型類可以讓我們在需要時傳入任何型別,在.net中,我們稱作型別引數。

3.型別引數約束

我們定義了一個Book類,對它實現排序,該怎麼實現呢?假設該類包含兩個欄位,一個int型別的單價price,一個string型別的title標題。

Book[] bookArray = new Book[2];
Book book1 = new Book(30, "HTML5解析");
Book book2 = new Book(21, "JavaScript實戰");
bookArray[0] = book1;
bookArray[1] = book2;
SortHelper<Book> sorter = new SortHelper<Book>();
sorter.BubbleSort(bookArray);
foreach (Book b in bookArray) {
Console.WriteLine("Price:{0}", b.Price);
Console.WriteLine("Title:{0}\n", b.Title);
}

那麼到底該怎麼進行比較大小呢? 是以價格為準呢,還是以書名的字母順序呢?都不好說,所以這個時候就需要定義一個比較規則了,那麼在.net中,實現比較的基本方法是實現IComparable介面。此介面有泛型和非泛型兩個。

public class Book :IComparable {
// CODE:上面的實現略
public int CompareTo(object obj) {
Book book2 = (Book)obj;
returnthis.Price.CompareTo(book2.Price);
}
}

這裡我們以price為準來進行排序,可以看到CompareTo()方法引數接受了一個object型別的引數,我們卻違背了Liskov替換原則。原則要求方法內部不應該對方法所接受的引數進行向下的強制轉換。我們都知道,在C#中,object是一切型別的基類。book類繼承自Object類,所以我們在將object轉換為book進行價格比較時,就稱作向下的強制轉換.

泛型類,所接受的T型別引數必須能夠進行比較--即實現IComparable介面,所以我們可以這樣定義:

public class SortHelper<T> where T:IComparable {
// CODE:實現略
}

4.泛型方法

既然有了泛型類,我們就引入了泛型方法的概念,泛型方法的引入主要是為了解決,我們在宣告泛型類時,避免因為呼叫某一個方法不得不將型別引數T傳給某個類,使得那些不需要呼叫該方法的情況下,建立一個類例項也需要傳遞型別引數的情況。

如下:SpeedSort()就是一個泛型方法

public class SortHelper
{
public void SpeedSort<T>(T[] Array) where T:IComparable
{
    
}
}

二.泛型和集合型別

1.避免隱式的裝箱拆箱

典型的非泛型集合ArrayList,它包容了任何型別, 所以它接受的引數為所有型別的基類Object, 當我們需要獲取到一個int[],需要迴圈遍歷ArrayList,並將它一一新增到我們所宣告的陣列集合中去,由於object是一個引用型別,而int是一個值型別,這樣在得到這麼一個包含int型別的ArrayList陣列時,我們就需要不斷的進行裝箱、拆箱的操作,這對.net來說是相對耗時的。

通過使用泛型,由於集合中的元素型別在編譯時就已確定,避免了裝箱和拆箱的操作,很顯著的提高集合型別的效能。 在.NET中,與ArrayList作用等同的泛型型別是 List.

2.編譯時的型別安全

由於使用泛型,集合中的元素在編譯時就已確定,所以當我們想將一個int型別的數值轉換為string時,編譯器會直接告訴我們編譯錯誤,這種在編譯時就檢查出的錯誤,叫做編譯時錯誤,與之相對的,就是我們的執行時錯誤,即當使用ArrayList時,因為它所接受的引數是object基類,編譯器視為將一個int型別的數值轉換為string時是正常的,只有執行時才檢查出錯誤。

三.小結

本章學習了為什麼需要泛型(避免重複程式碼)以及如何使用型別引數約束和泛型方法,並且比較了泛型集合List和非泛型集合ArrayList的對比,熟悉並瞭解了泛型集合的優勢和應用。

很開心,又跟隨作者學習了第二章泛型的概念,上一篇讀書筆記中,很多小夥伴給我留言了,說了很多鼓勵的話,很感謝,能和大家一起進步學習,其中有一位小夥伴提到的方法我覺得很棒,就是大量閱讀開源原始碼,確實,我在園子裡看到很多對底層原理理解很透徹的大佬們,每每講解一個知識點時,總是會教我們去跟蹤原始碼,即理解它的底層實現邏輯是啥,這樣更能加深我們對知識點的理解和鞏固。

我也是在學習中,希望自己在今後的學習和工作中,能掌握這種技巧,幫助自己更好的理解並熟悉它的原理。

相關文章