久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

【讀書筆記】C#高級(jí)編程 第五章 泛型

 正則室 2017-03-27

(一)泛型概述

泛型不僅是C#編程語言的一部分,而且與程序集中的IL代碼緊密地集成,。泛型不僅是C#語言的一種結(jié)構(gòu),,而且是CLR定義的。有了泛型就可以創(chuàng)建獨(dú)立于被包含類型的類和方法了,。

1,、性能

泛型的一個(gè)主要優(yōu)點(diǎn)就是性能,。對(duì)值類型使用非泛型集合類,在把值類型轉(zhuǎn)化為引用類型,,和把引用類型轉(zhuǎn)換為值類型時(shí),,需要進(jìn)行裝箱和拆箱操作。

下面的例子顯示了System.Collections名稱空間中的ArrayList類,。ArrayList存儲(chǔ)對(duì)象,,Add()方法定義為需要把對(duì)象作為參數(shù),所以要裝箱一個(gè)整數(shù)類型,。在讀取ArrayList中的值時(shí),,要進(jìn)行拆箱操作,把對(duì)象轉(zhuǎn)化為整數(shù)類型,??梢允褂妙愋脱b置轉(zhuǎn)換運(yùn)算符把ArrayList集合的第一個(gè)元素賦予變量i1,在訪問int類型的變量i2foreach語句中,,也要使用類型強(qiáng)制轉(zhuǎn)換運(yùn)算符:

 

復(fù)制代碼
var list = new ArrayList();list.Add(44);//此處會(huì)裝箱int i1 = (int)list[0];//此處會(huì)拆箱foreach (int i2 in list){ Console.WriteLine(i2);//此處會(huì)拆箱}
復(fù)制代碼

 

System.Collections.Generic名稱空間中的List類不使用對(duì)象,,而是在使用時(shí)定義類型。裝箱和拆箱操作很容易,,但性能損失比較大,,遍歷許多項(xiàng)尤其如此。

下面的例子,。List類的泛型類型定義為int,,所以int類型在JIT編譯器動(dòng)態(tài)生成的類中使用,不再進(jìn)行裝箱和拆箱操作:

復(fù)制代碼
var list = new Listint>();list.Add(44);int i1 = (int)list[0];foreach (int i2 in list){ Console.WriteLine(i2);}
復(fù)制代碼

 

 

2,、類型安全 

泛型的另一個(gè)特性是類型安全,。

在泛型類List中,泛型類型T定義了允許使用的類型,。有了List的定義,,就只能把整數(shù)類型添加到集合中。編譯器不會(huì)編譯這段代碼,,因?yàn)?/span>Add()方法無效,,這樣類型就安全了:

復(fù)制代碼
var list = new Listint>();list.Add(44);list.Add('str');
復(fù)制代碼

這個(gè)時(shí)候編譯器會(huì)報(bào)錯(cuò):

 

 

 

3,、二進(jìn)制代碼的重用

泛型允許更好地重用二進(jìn)制代碼,。泛型類可以定義一次,并且可以用許多不同的類型實(shí)例化,。

例如,,System.Collections.Generic名稱空間中的List類用一個(gè)int、一個(gè)字符串和一個(gè)MyClass類實(shí)例化:

復(fù)制代碼
var intList = new Listint>();intList.Add(1); var stringList = new Liststring>();stringList.Add('str');var myClassList = new List();myClassList.Add(new MyClass());
復(fù)制代碼

 

4,、代碼的擴(kuò)展 

在不同的特定類型實(shí)例化泛型時(shí),,會(huì)創(chuàng)建多少代碼,?因?yàn)榉盒皖惖亩x會(huì)放在程序集中,所以用特定類型實(shí)例化泛型類不會(huì)再IL代碼中賦值這些類,。但是,,JIT編譯器把泛型類編譯為本地代碼時(shí),會(huì)給每個(gè)值類型創(chuàng)建一個(gè)新類,。引用類型共享同一個(gè)本地類的所有相同的實(shí)現(xiàn)代碼,。這是因?yàn)橐妙愋驮趯?shí)例化的泛型類中只需要4個(gè)字節(jié)的內(nèi)存地址(32位系統(tǒng)),就可以引用一個(gè)引用類型,。值類型包含在實(shí)例化的泛型類的內(nèi)存中,,同時(shí)因為每個(gè)值類型對(duì)內(nèi)存的要求都不同,所以要為每個(gè)值類型實(shí)例化一個(gè)新類,。

 

5,、命名的約定

在程序中使用泛型,在區(qū)分泛型類型和非泛型類型時(shí)就會(huì)有一定的幫助,。下面是泛型類型的命名規(guī)則:

泛型類型的名稱用字母T作為前綴,。

如果沒有特殊的要求,泛型類型允許使用任意類替代,,且只使用了一個(gè)泛型類型,,就可以用字符T作為泛型類型的名稱。

復(fù)制代碼
public class List { }public class LinkedList { }
復(fù)制代碼

如果泛型類型有特定的要求(例如,,它必須實(shí)現(xiàn)一個(gè)接口或派生自基類),,或者使用了兩個(gè)或多個(gè)泛型類型,就應(yīng)給泛型類型使用描述性的名稱: 

復(fù)制代碼
public class SortedList { }
復(fù)制代碼

 

(二)創(chuàng)建泛型類

泛型提供了一種新的創(chuàng)建類型的機(jī)制,,使用泛型創(chuàng)建的類型將帶有類型形參,。每個(gè)處理對(duì)象類型的類都可以有泛型實(shí)現(xiàn)方式。另外如果類使用了層次結(jié)構(gòu)就非常有助于消除類型強(qiáng)制轉(zhuǎn)換操作,。

復(fù)制代碼
public class LinkedListNode{ public LinkedListNode(T value) { this.Value = value; } public T Value { get; private set; } public LinkedListNode Next { get; internal set; } public LinkedListNode Prev { get; internal set; }}
復(fù)制代碼

 

 

(三)泛型類的功能

1,、默認(rèn)值

通過default關(guān)鍵字,將null賦予引用類型,,將0賦予值類型,。

 

復(fù)制代碼
public T GetDocument(){ T doc = default(T); return DealDocument(doc);}
復(fù)制代碼

 

default關(guān)鍵字根據(jù)上下文可以有多種含義。switch語句使用default定義默認(rèn)情況,。在泛型中,,根據(jù)泛型類型是引用類型還是值類型,泛型default用于將泛型類型初始化為null0,。

 

2,、約束

在定義泛型類時(shí),可以對(duì)客戶端代碼能夠在實(shí)例化類時(shí)用于類型參數(shù)的類型種類施加限制,。如果客戶端代碼嘗試使用某個(gè)約束所不允許的類型來實(shí)例化類,,則會(huì)產(chǎn)生編譯時(shí)錯(cuò)誤,。這些限制稱為約束。約束是使用 where 上下文關(guān)鍵字指定的,。下表列出了六種類型的約束:

約束

說明

where T:struct

類型參數(shù)必須是值類型,。可以指定除 Nullable 以外的任何值類型,。

where T:Class

類型參數(shù)必須是引用類型,,包括任何類、接口,、委托或數(shù)組類型,。

where T:new()

類型參數(shù)必須具有無參數(shù)的公共構(gòu)造函數(shù)。當(dāng)與其他約束一起使用時(shí),,new() 約束必須最后指定,。

where T:基類名>

類型參數(shù)必須是指定的基類或派生自指定的基類。

where T:接口名稱>

類型參數(shù)必須是指定的接口或?qū)崿F(xiàn)指定的接口,??梢灾付ǘ鄠€(gè)接口約束。約束接口也可以是泛型的,。

where T1:T2

T1 提供的類型參數(shù)必須是為 T2 提供的參數(shù)或派生自為 T2 提供的參數(shù),。這稱為裸類型約束。

使用約束的原因

如果要檢查泛型列表中的某個(gè)項(xiàng)以確定它是否有效,,或者將它與其他某個(gè)項(xiàng)進(jìn)行比較,,則編譯器必須在一定程度上保證它需要調(diào)用的運(yùn)算符或方法將受到客戶端代碼可能指定的任何類型參數(shù)的支持。這種保證是通過對(duì)泛型類定義應(yīng)用一個(gè)或多個(gè)約束獲得的,。例如,,基類約束告訴編譯器:僅此類型的對(duì)象或從此類型派生的對(duì)象才可用作類型參數(shù)。一旦編譯器有了這個(gè)保證,,它就能夠允許在泛型類中調(diào)用該類型的方法,。約束是使用上下文關(guān)鍵字 where 應(yīng)用的。

使用泛型類型還可以合并多個(gè)約束:

復(fù)制代碼
public class MyClass where T : IClass, new() { }
復(fù)制代碼

 

3,、繼承

泛型類型可以實(shí)現(xiàn)泛型接口,,也可以派生自一個(gè)類(要求是必須重復(fù)接口或基類的泛型類型):

復(fù)制代碼
public class LinkedList : IEnumerable { }public class MyClass : MyBaseClass { }
復(fù)制代碼

派生類也可以是泛型或非泛型的,其要求是必須制定基類的類型

復(fù)制代碼
public class IntClass : MyBaseClassint> { }
復(fù)制代碼

 

4,、靜態(tài)成員

泛型類的靜態(tài)成員只能在類的一個(gè)實(shí)例中共享:

復(fù)制代碼
public class StaticDemo{ public static int x;}static void Main(string[] args){ StaticDemostring>.x = 1; StaticDemoint>.x = 2; Console.WriteLine(StaticDemostring>.x); Console.WriteLine(StaticDemoint>.x); Console.ReadKey();}
復(fù)制代碼

 運(yùn)行以上代碼,,結(jié)果如下:

當(dāng)T類型不同時(shí)靜態(tài)成員不共享。

 

(四)泛型接口

使用泛型可以定義接口,,在泛型接口中定義的方法可以帶泛型參數(shù),。

復(fù)制代碼
public interface IDeal{ T Deal(T value);}
復(fù)制代碼

 

協(xié)變和抗變指對(duì)參數(shù)和返回值類型進(jìn)行轉(zhuǎn)換。在.NET中參數(shù)類型時(shí)協(xié)變的,,返回類型是抗變的,。

 

1、協(xié)變和抗變

例子:

參數(shù)類型的協(xié)變

復(fù)制代碼
static void Main(string[] args){ string str = '測(cè)試參數(shù)的協(xié)變'; Show(str); Console.ReadKey();}public static void Show(object value){ Console.WriteLine(value);}
復(fù)制代碼

返回類型的抗變

例子:

復(fù)制代碼
static void Main(string[] args){ int value = 1; string str = ConvertToString(value); Console.ReadKey();}public static string ConvertToString(object value){ return value.ToString();}
復(fù)制代碼

 

2,、泛型接口的協(xié)變 

如果泛型類型用out關(guān)鍵字進(jìn)行標(biāo)注,,泛型接口就是協(xié)變的。(子到父是協(xié)變)

復(fù)制代碼
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 IFactory ChineseFactory = new Factory(); 6 IFactory PeopleFactory = ChineseFactory; //協(xié)變 7 People People = PeopleFactory.Create(); 8 Console.ReadKey(); 9 }10 }11 public class Chinese : People { }12 public class People { }13 public class Factory : IFactory14 {15 public T Create()16 {17 return (T)Activator.CreateInstance();18 }19 }20 public interface IFactoryout T>21 {22 T Create();23 }
復(fù)制代碼

 

3,、泛型接口的抗變

如果泛型類型用in關(guān)鍵字標(biāo)注,,泛型接口就是抗變的。

復(fù)制代碼
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 IShow ps = new PeopleShow(); 6 IShow cs = ps;//抗變 7 Console.ReadKey(); 8 } 9 }10 public interface IShowin T>11 {12 void Write(T t);13 }14 public class Chinese : People { }15 public class People16 {17 public string Name { get; set; }18 }19 public class PeopleShow : IShow20 {21 public void Write(People t)22 {23 Console.WriteLine('我的名字:' + t.Name + ',,現(xiàn)在我表演寫字,!');24 }25 }
復(fù)制代碼

 

 

(五)泛型結(jié)構(gòu)

與類相似,結(jié)構(gòu)也可以是泛型的,。它們非常類似于泛型類,,只是沒有繼承特性。.NET Framework中的一個(gè)泛型結(jié)構(gòu)是Nullable,,結(jié)構(gòu)Nullable定義了一個(gè)約束:其中泛型類型T必須是一個(gè)結(jié)構(gòu),。

因?yàn)榭煽疹愋褪褂玫梅浅nl繁,所以C#有一種特殊的語法,,它用于定義可空類型的變量,。定義這類變量時(shí),不使用泛型結(jié)構(gòu)的語法,,而是用“?”運(yùn)算符,。

int? x;

可以使用合并運(yùn)算符從可空類型轉(zhuǎn)換為非可空類型。合并運(yùn)算符??

復(fù)制代碼
int? x = null;int y = x ?? 0;
復(fù)制代碼

 y的值顯示為0,,因?yàn)?/span>xnull,。

 

 

(六)泛型方法

除了定義泛型類之外,還可以定義泛型方法,。在泛型方法中,,泛型類型用方法聲明來定義。泛型方法可以在非泛型類中定義,。

復(fù)制代碼
public T ReviseName(T person) where T : People{ person.Name = '改名后的:' + person.Name; return person;}
復(fù)制代碼

C#編譯器會(huì)通過泛型方法來獲取參數(shù)類型,,所以不需要把泛型類型賦予方法的調(diào)用。

復(fù)制代碼
Chinese chinese = new Chinese();chinese.Name = '張三';Chinese.ReviseName(chinese);
復(fù)制代碼

泛型方法可以像泛型方法那樣調(diào)用,。

 

1,、泛型方法示例

復(fù)制代碼
1 class Program 2 3 { 4 5 static void Main(string[] args) 6 7 { 8 9 var accounts = new List()10 11 {12 13 new Account('張三',2000),14 15 new Account('李四',1300),16 17 new Account('王麻子',800),18 19 new Account('趙六',1000)20 21 };22 23 decimal total = AccumulateSimple(accounts);24 25 Console.ReadKey();26 27 }28 29 public static decimal AccumulateSimple(IEnumerable source)30 31 {32 33 decimal sum = 0;34 35 foreach (var item in source)36 37 {38 39 sum += item.Balance;40 41 }42 43 return sum;44 45 }46 47 }48 49 public class Account50 51 {52 53 public string Name { get; set; }54 55 public decimal Balance { get; set; }56 57 public Account(string name, Decimal balance)58 59 {60 61 this.Name = name;62 63 this.Balance = balance;64 65 }66 67 }
復(fù)制代碼

 

2、帶約束的泛型方法

復(fù)制代碼
public T ReviseName(T person) where T : People{ person.Name = '改名后的:' + person.Name; return person;}
復(fù)制代碼

 因?yàn)榉椒ㄐ枰褂?/span>T參數(shù)的Name屬性,,所以為了確保程序不拋出異常,,對(duì)T參數(shù)進(jìn)行約束,使其必須繼承自People類,。

 

3,、帶委托的泛型方法

復(fù)制代碼
1 static void Main(string[] args) 2 { 3 int t1 = 1; 4 int t2 = 9; 5 Console.WriteLine(Cal(t1, t2, (i1, i2) => 6 { 7 return i1 + i2; 8 })); 9 Console.ReadKey();10 }11 12 public static T Cal(T t1, T t2, Func calMethod)13 {14 return calMethod(t1, t2);15 }
復(fù)制代碼

  定義Cal方法,,參數(shù)t1t2calMethod方法的參數(shù)(Func是內(nèi)置的委托),。

 

4,、泛型方法規(guī)范

泛型方法可以重載,為特定的類型定義規(guī)范,。在編譯期間會(huì)使用最佳匹配,。

復(fù)制代碼
1 static void Main(string[] args) 2 { 3 string str = 'str'; 4 int i = 0; 5 First(str); 6 First(i); 7 Console.ReadKey(); 8 } 9 10 public static void First(T obj)11 {12 Console.WriteLine('obj:'+obj.GetType().Name);13 }14 15 public static void First(int obj)16 {17 Console.WriteLine('int');18 }
復(fù)制代碼

 運(yùn)行以上代碼,結(jié)果如下:

需要注意的是,,所調(diào)用的方法是在編譯期間定義的,,而不是運(yùn)行期間。

復(fù)制代碼
static void Main(string[] args){ int i = 0; Second(i); Console.ReadKey();}public static void Second(T obj){ First(obj);}
復(fù)制代碼

 運(yùn)行以上代碼,,結(jié)果如下:

 

 

 

(七)小結(jié)

本章介紹了CLR中一個(gè)非常重要的特性:泛型,。通過泛型類可以創(chuàng)建獨(dú)立于類型的類,泛型方法是獨(dú)立于類型的方法,。接口,、結(jié)構(gòu)和委托也可以用泛型的方式創(chuàng)建。泛型引入了一種新的編程方式,。我們介紹了如何實(shí)現(xiàn)相應(yīng)的算法(尤其是操作和謂詞)以用于不同的類,,而且它們都是類型安全的。泛型委托可以去除集合中的算法,。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn),。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購買等信息,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多