當前位置:
首頁 > 知識 > 看完這篇,終於知道自己會不會 C# 泛型了

看完這篇,終於知道自己會不會 C# 泛型了

看完這篇,終於知道自己會不會 C# 泛型了

看完這篇,終於知道自己會不會 C# 泛型了

作者 | 羽生結弦

責編 | 胡巍巍

在開發過程中,同一段代碼處出現多次調用,並且會有不同的類型在使用,這種就叫做跨類型代碼復用。一般情況下跨類型代碼復用我們會用到如下兩種方法:

1. 繼承;

2. 泛型。

繼承是通過父類來代表代碼復用,而泛型是通過帶有佔位符的模板來代碼復用,其中佔位符指的是類型。

比如:int、syting和實體等。本片主要講解泛型的相關知識,下面就來詳細講解一下泛型。

看完這篇,終於知道自己會不會 C# 泛型了

零、泛型類型

泛型會聲明類型參數,消費者需要提供類型參數來把佔位符類型填充上。我們先來看一個例子:

public class GenericClass<T>

{

T tArray=new T[10];

int position=0;

public void Push(T t) =>tArray[position++]=t;

public T Pop=>tArray[--position];

public T GetTs => tArray;

}

class Program

{

static void Main(string[] args)

{

var genericStr=new GenericClass<string>;

generic.Push("張三");

generic.Push("李四");

generic.Push("王五");

generic.Pop;

var genericInt=new GenericClass<int>;

generic.Push(1);

generic.Push(2);

generic.Push(3);

generic.Pop;

}

}

在上面的代碼中當我們將string傳入泛型類中,將會隱式動態的創建類型,這種操作被稱為合成,合成將發生在運行時,而非編譯時。當我們在代碼中傳入非string類型的值時,在編譯時將報錯。同樣在上面的代碼中我們也看到了int傳入泛型類中的情況,這就說明泛型類可以跨類型復用。

小知識1:

我們將GenericClass<T>稱為開放類型(OpenType),而將GenericClass<string>稱為封閉類型(CloseType),開放類型在編譯後就變成了封閉類型,在運行時所有的泛型類型都是封閉類型,因為佔位符已經被具體類型填充完畢。

小知識2:

對於每一種封閉類型,靜態數據都是唯一的,例如:

class Program

{

static void Main(string[] args)

{

// 輸出1

Console.WriteLine(++MyClass<int>.Count);

// 輸出2

Console.WriteLine(++MyClass<int>.Count);

// 輸出1

Console.WriteLine(++MyClass<string>.Count);

}

}

class MyClass<T>

{

public static int Count;

}

上面代碼中,MyClass中存在一個靜態欄位Count,我們看到前兩次的調用輸出的分別是1和2,但是第三次輸出的確實1,那麼這是為什麼呢,原因就是前兩次的類型參數和泛型類型種的靜態欄位的類型一致,而第三次的類型參數不一致導致的。

在泛型類型餓子類中可以繼續讓父類參數保持開放,也剋及關閉父類的類型參數,同樣子類也可以引入新的類型,我們來看一下例子:

// 父類繼續保持開放

class Father<T>{}

class Children<T>:Father<T>{}

//關閉父類

class Father<T>{}

class Children:Father<string>{}

//引入新參數類型

class Father<T>{}

class Children<T,U>:Father<T>{}

小知識:

封閉參數類型的時候,該類型可以把自己作為具體的類型,例子如下:

class AClass<T>{}

class BClass : AClass<BClass> { }

看完這篇,終於知道自己會不會 C# 泛型了

泛型方法

泛型方法在方法簽名內聲明類型參數,例如下:

class Program

{

static void Main(string[] args)

{

GenericFun<int>(12, 4);

}

static void GenericFun<T>(T x, T y)

{

T tmp = x;

x = y;

y = tmp;

Console.WriteLine("x:" + x + " y:" + y);

}

}

在上面的代碼中我們看到 ```generic.GenericFun<int>(12,4);``` ,我們將int傳入泛型方法中,現在我們將這行代碼改寫成如下形式 ```generic.GenericFun(12,4);```。

我們發現現在這段代碼和前面那段代碼缺少了 <int> ,那麼這麼寫代碼是否有錯呢?

答案是要看情況,當編譯器可以推斷出參數類型的話,我們可以省略掉參數類型,但是當編譯器無法推斷出參數類型的話我們就必須寫上參數類型了。

當然上面這段代碼是可以正確運行的,因為編譯器可以正確的推斷出參數類型。我們還有如下幾點需要注意的:

1. 泛型類中的方法,如果方法引入了參數類型,那它就是泛型方法,反之就不是泛型方法;

2. 除了class、struct、interface、delegate 和方法可以引入類型參數外,屬性、欄位、索引器、事件和構造函數等都不能聲明類型參數,但是可以使用所在泛型類的類型參數。

小知識:

泛型類型和泛型方法可以有多個參數類型,例如 ```class a<T,U>``` 調用方法和單個參數類型一樣。結合這一點我們就可以推斷出泛型類型和泛型方法可以出現重載,只要參數類型的數量不同就沒問題。

在一些情況下我們需要獲取參數類型的默認值,這時我們就可以使用default(T)來獲得。

看完這篇,終於知道自己會不會 C# 泛型了

約束

我們雖然可以使用所有類型作為泛型類型參數,但是我們在實際開發時很少會這麼使用,一般會將類型參數約束到指定的範圍內。泛型可用約束如下:

1. base-class:某一個父類的子類;

2. interface:必須是實現了指定的介面;

3. class:必須是引用類型;

4. struct:必須是非空值類型;

5. new:必須包含無參構造函數;

6. U:T:U必須繼承T

我們使用的時候是這樣的:

class Aclass {}

interface Binterface { }

class Generic1<T> where T : Aclass { }

class Generic2<T,U>

where T:Aclass,Binterface

where U : new

{ }

上面的代碼表示 Generic1 類的參數類型T繼承自Aclass,Generic2 類的T繼承子Aclass,並且實現了 Binterface 介面,而且U包含了無參構造函數。

注意:約束可以用於泛型類型和泛型方法

看完這篇,終於知道自己會不會 C# 泛型了

類型參數與轉換

C#種轉換支持如下幾種:

1. 數值轉換;

2. 引用轉換;

3. 裝箱拆箱轉換;

4. 自定義轉換。

在發生編譯的時候,會根據一直類型的操作數來決定採用那種轉換,但是在泛型中我們不知道具體的類型是什麼,編譯器就會默認使用自定義轉換,那麼就有可能出現錯誤。

為了解決這個問題,我們引入了as ,例如我們將傳進來的值轉換成StringBuilder,這時我們可以這麼做:

StringBuilder ToFloat<T>(T t)

{

StringBuilder f = t as StringBuilder;

return f;

}

看完這篇,終於知道自己會不會 C# 泛型了

variance 轉換

講解variance 轉換前,先來簡單了解一下協變、逆變和不變。

1. 協變(Covariance):當T作為返回值輸出的時候;

2. 逆變(Contravariance):當T作為輸出值的時候;

3. 不變(Invariance):當T即是輸入又是輸出時。

注意:以上這三種就是variance,只能用在介面和委託中。

所謂variance 轉換就是上面三種之間的相互轉換,variance 轉換是引用轉換的一個方法,從a轉換到b如果是本體轉換或者隱式引用轉換,那麼就是正確的。例如:

IEnumerable<string> to IEnumerable<object>

IEnumerable<IDisposable> to IEnumerable<object>

作者簡介:朱鋼,筆名羽生結弦,CSDN博客專家,.NET高級開發工程師,7年一線開發經驗,參與過電子政務系統和AI客服系統的開發,以及互聯網招聘網站的架構設計,目前就職於北京恆創融慧科技發展有限公司,從事企業級安全監控系統的開發。

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 CSDN 的精彩文章:

不要讓 Chrome 成為下一個 IE
2019 年度程序員吸金榜:你排第幾?

TAG:CSDN |