.NET 中的泛型 101

1.1.1 摘要

generic0

圖1 C# 泛型介紹

????? 在接觸泛型之前,我們編程一般都是使用具體類型(char, int, string等)或自定義類型來定義我們變量,如果我們有一個功能很強的接口,而且我們想把它提取或重構成一個通用的接口,使得該接口不僅僅適用于已定義數據類型,而是適用于更多數據類型,從而方便以后的擴展。

???? 泛型提供上述功能的實現,泛型其實就是提供一個數據類型的抽象層,因為它泛所以抽象,方便了我們代碼的重構和提取,我們無需hard-code接口中的數據類型,而是通過一個抽象泛型類型來指定數據類型,所以泛型可以提取出一個通用的接口。接下來讓我們通過具體的例子說明什么是泛型。

1.1.2 正文

1.1.1.1 沒有泛型的C# 1.0

????? 相信許多人都使用過棧或者其他經典數據結構,而且對于棧的FILO都是嫻熟于心了。假設現在要我們定義一個棧,用來存放具體數據類型,例如:int 數據,OK讓我們來實現自定義棧。

/// <summary>
/// A custom stack1.
/// </summary>
public class CustomStack1
{private int _stackPointer = 0;private int[] _stackArray;public void Push(int item){Concrete implemention.}public int Pop(){Concrete implemention.}
}

????? 為了簡潔沒有完全遵守OOP的原則,這里沒有使用屬性和給出方法的具體實現,現在我們有了第一版可以存放int數據類型的棧。但如果需求改變保存數據類型增加string。那么我們可以怎樣實現呢?沒錯我們只要增加一個類型為string的棧就OK了。

/// <summary>
/// A custom stack2.
/// </summary>
public class CustomStack2
{private int _stackPointer = 0;private string[] _stackArray;public void Push(int item){Concrete implemention.}public string Pop(){Concrete implemention.}
}

????? 很快我們就完成了第二版本的棧了,這看上去的確很簡單而且實現起來很快只不過是ctrl + c和ctrl + v,但我們的代碼真的那么簡單好維護嗎?想想其實危機四伏。我們能不能一開始就把保存的數據類型定義成為一個通用的類型呢(抽象數據類型)?----邪惡的object。

/// <summary>
/// A custom stack.
/// </summary>
public class CustomStack
{private int _stackPointer = 0;private object[] _stackArray;public void Push(object item){Concrete implemention.}public object Pop(){Concrete implemention.}
}

????? 現在我們實現了第三版的棧了,看上去我們好像找到了通用的解決方法,想想我們要注意一點是如果我們保存和取出數據類型為值類型時,值類型要進行boxing和unboxing操作(《.NET值類型和引用類型101》),而且還有進行數據類型檢測。如果數據量不大我們可能察覺不到性能變化,但數據量大hehehe…。這就是為什么我們要使用泛型的原因之一,但更重要的原因是在進行boxing和unboxing時,我們需要提供更多信息給編譯器,編譯時再根據我們提供的信息進行類型檢測。

1.1.1.2 C#中的泛型

????? 現在我們知道了.NET為什么要提供泛型,現在讓我們學習什么是泛型和怎樣實現吧!在C# 2.0中提供了泛型這一機制,但這可不是什么新奇的技術,因為早在C++和Java都提供泛型機制。

C#中提供五種泛型分別是:classes, structs, interfaces, delegates, and methods。

  • 泛型類

???? 也許有人說沒有使用過C++的模板或Java的泛型,而且不了解C#中的泛型,真的是這樣嗎?其實我們經常都在使用泛型,相信很多人都使用過Dictionary<TKey,TValue>,沒錯這就是C#中提供的泛型字典,接下來我們將介紹自定義泛型類CustomStack。

generic2圖2泛型類?

/// <summary>
/// A custom generic stack.
/// </summary>
public class CustomStack<T>
{private int _stackPointer = 0;private T[] _stackArray;public void Push(T item){Concrete implemention.}public T Pop(){Concrete implemention.}
}

????? 前面給出了泛型類CustomStack實現,我們發現只需在類名后添加<T>,并且把具體數據類型改為抽象的泛型類型T,現在我們就定義了一個簡單泛型類,我們可以通過以下對比一下非泛型和泛型實現的區別。

generic1

圖3非泛型和泛型類定義

????? 通過上圖我們發現泛型類CustomStack和非泛型類CustomStack在實現上沒有太大的區別,我們只需把具體類型換成T就OK了,接下來我們將進入泛型的進階學習。

  • 泛型委托

???? 關于委托大家可以閱讀《.NET 中的委托》和《.NET中的委托和事件(續)》,現在讓我們學習一下泛型委托。

generic4

圖4泛型委托的定義

????? 上圖給出了泛型委托的定義,我們要注意Type parameters包括泛型參數和返回類型。隨著C#3.0 Linq特性的引用,泛型委托的使用得到了極大地擴展,讓我們通過具體代碼講講如何使用泛型委托。

????? 首先我們定義一個泛型委托,然后再定義一個Calc類,接著在Calc里定義兩個方法分別是Add() 和Divide() 如下:

/// <summary>
/// Define generic delegate.
/// </summary>
public delegate TR CalcMethod<T1, T2, TR>(T1 x, T2 y);public class Calc
{static private double sum;/// <summary>/// Gets or sets the sum./// </summary>/// <value>/// The sum./// </value>static public double Sum{get { return sum; }set { sum = value; }}/// <summary>/// Adds the specified x./// </summary>/// <param name="x">The x.</param>/// <param name="y">The y.</param>/// <returns></returns>static public string Add(int x , int y){Sum = x + y;return Sum.ToString();}/// <summary>/// Divides the specified x./// </summary>/// <param name="x">The x.</param>/// <param name="y">The y.</param>/// <returns></returns>static public string Divide(int x, int y){if (y == 0){return "除數不能為零";}Sum = x * 1.0 / y;return Sum.ToString();}} 

??? 接下來我們需要定義一個委托變量,而后將委托變量和具體調用方法綁定就OK了。

Initialize engeric delegate instance to Add method.
CalcMethod<int, int, string> calcMethod =new CalcMethod<int, int, string>(Calc.Add);
  • 類型約束

??? 前面例子我們并沒有對泛型類型進行限制,由于T是一個抽象類型,有時我們必須限制T是哪種類型,例如我們可以定義CustomStack<int>,CustomStack<double>或CustomStack<string>類型的棧,但有時我們必須限制泛型類型。這時我們需要使用類型約束,引入關鍵字----where。

??? C#中提供五種類型約束(類名,接口名,引用類型,值類型和構造函數),我們可以通過使用不同的類型約束來限定泛型類型。

類型約束

約束描述

ClassName

T只能是該類或繼承該類的子類

InterfaceName

T只能是該接口或實行該接口的類型

struct

T是任意值類型

class

T是任意引用類型(如classes, arrays, delegates, and interfaces等)

new()

T是包含公有無參構成函數的任意類型

表1類型約束

?

????? 通過表1我們初步地了解不同類型約束之間的區別,現在讓我們通過具體的例子來看看它們使用范圍和區別。

????? 假設我們定義了一個泛型結構體struct RefSample<T> where T : class,現在考考大家以下例子符合類約束的有(提示值類型和引用類型區別):

?

A. ? RefSample<IDisposable>

B. ? RefSample<string>

C.? RefSample<int[]>

D.? RefSample<Guid>

E. ? RefSample<int>

???? 激動人心的時刻又到了現在讓我們快快公布答案吧!答案是ABC,因為該泛型的約束是引用類型約束,如果大家還不清楚請看一下引用類型約束的定義。

???? 通過類約束例子,我們舉一反三值類型約束就是約束類型為值類型。

???? OK接下來讓我們介紹構造函數約束。在介紹構造函數約束之前,讓我們回顧一下符合構造函數約束的條件:任意值類型,任意非靜態非抽象無明確定義構造函數的類和任意非抽象有明確定義無參構造函數的類。這句話很別扭,讓我們通過具體例子講解一下。

 Define generic methodWhich has constructor type constraints
public T CreateInstance<T>() where T : new() 
{
return new T(); 
}CreateInstance<int>();
CreateInstance<object>();
CreateInstance<string>();

????? 現在我們定義了一個泛型方法并且添加構造函數約束,調用CreateInstance<int>() 和 CreateInstance<object>() 成功,但調用CreateInstance<string>() 失敗,這由于String類沒有提供無參構造函數。也許有人會問:“我們很難判斷每種類型是否包含默認構無參造函數”,確確是這樣但我們可以記住的是C#規定值類型提供默認無參構造函數。

?

  • 組合約束

???? 前面的泛型例子都是只使用一種類型約束,但有時候我們要使用多種類型約束,這時我們就可以使用組合約束,在使用約束的時我們要注意約束的先后次序,約束次序如下:

generic3

圖5約束次序

???? 通過上圖我們發現ClassName,class和struct是主要約束,接口名是第二約束,最后是構造函數約束。OK現在我們對組合約束有了初步地了解,那么讓我們通過以下例子加深理解。

A. class Sample<T> where T : class, struct

B. class Sample<T> where T : Stream, class

C. class Sample<T> where T : new(), Stream

D. class Sample<T> where T : IDisposable, Stream

E. class Sample<T> where T : XmlReader, IComparable, IComparable

F. class Sample<T,U> where T : struct where U : class, T

G. class Sample<T,U> where T : Stream, U : IDisposable

???? 上面給出錯誤使用組合約束的例子(注:A~E單類型組合約束,FG多類型組合約束),請大家指出約束錯誤的原因,如果不清楚錯誤的原因大家回憶一下約束定義。

1.1.3 總結

???? 本文注意介紹了C# 中泛型的基礎知識給出自己的總結,而且這不是C# 泛型的全部,如果大家在看完本文之后希望進一步學習泛型這才是我的目的。

泛型的優點:

  • 編譯時進行類型檢測,減少運行時異常InvalidCastException。
  • 泛型使用強數據類型。如我們實例一個CustomStack<CustomStruct>對象objStack,通過調用Push()方法成功把CustomStruct壓入棧中,而objStack.Push(“Stack”)失敗。
  • 值類型無需進行boxing和unboxing操作。例如前面的泛型Push()和Pop()方法存放和取出值類型時無需進行boxing和unboxing操作。
  • 減少代碼量。如:我們定義一個泛型棧,就不用像前面例子那樣分別定義int和string類型的棧。
  • 程序性能提高,因為無需類型轉換,從而減少類型檢測。
  • 使用泛型減少內存消耗。因為無需進行boxing操作,不用重新分配堆空間。
  • 代碼可讀性更強。

????? 很多人喜歡將C# ,C++ 和Java中的泛型進行對比,而且也有很多相關的討論,在這里我也給出自己的想法,我覺得C++ 的泛型功能的確比C# 和Java都要強大。

Creative Commons License

[作者]:JK_Rush
[出處]:http://www.cnblogs.com/rush/
[本文基于]:?署名-非商業性使用 3.0?許可協議發布,歡迎轉載,演繹,但是必須保留本文的署名?JK_Rush?(包含鏈接),且不得用于商業目的。如您有任何疑問或者授權方面的協商,請與我聯系 。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/276403.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/276403.shtml
英文地址,請注明出處:http://en.pswp.cn/news/276403.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

年底了,給想進階的的前端朋友一些福利

2020 年&#xff0c;很多朋友都經歷了一段比較艱難的求職季。年末&#xff0c;“就業寒冬”迎來了一絲暖陽&#xff0c;很多中大型互聯網公司擴大了未來一年的招聘需求。前不久&#xff0c;字節跳動放出了年末要招 1 萬人的消息&#xff0c;騰訊校招規模也將擴張至 5000 人&…

python oa系統_用python把C#操作OA的例子重寫了一下

#手工chrome.exe --remote-debugging-port9222 --user-data-dir"C:\selenum\AutomationProfile"fromselenium import webdriverfromselenium.webdriver.common.by import Byfromselenium.webdriver.support.ui import WebDriverWaitfromselenium.webdriver.chrome.op…

編譯安裝PHP出現configure: error: MySQL configure failed. Please check config.log的解決方法

以下為google的結果&#xff1a; 方案一&#xff1a; 轉載鏈接&#xff1a;http://www.php-oa.com/2008/03/28/php-make.html 好久沒有編譯安裝過php了,為了玩nginx.沒法子,編譯一次來測試.我加的編譯的參數是: # ./configure –prefix/usr/local/php –with-config-file…

[Android]?Android學習手記(二)

1。SDK源碼獲取Git這個版本控制還真是第一次聽到。Linux參考官網&#xff08;需要穿墻&#xff09;的Get source好像比較容易。Windows就比較麻煩了&#xff0c;不能通過repo方式獲取整個projects&#xff0c;只能一個獲取project。不過官網稱“The source is approximentely 2…

關于分區索引與全局索引性能比較的示例

說明&#xff1a;之前使用range分區做出來的效果不明顯&#xff0c;這次使用hash分區。 1、準備工作&#xff1a; ----創建兩張一樣的hash分區表&#xff0c;jacks_part和echos_part------------------ 1 SQL> create table jacks_part (owner varchar2(30),object_id numbe…

Vue Router 4.0 正式發布!煥然一新。

關注若川視野&#xff0c;回復"pdf" 領取資料&#xff0c;回復"加群"&#xff0c;可加群長期交流學習12月8日&#xff0c;Vue Router 4 正式發布穩定版本。在經歷了 14 個 Alpha&#xff0c;13 個 Beta 和 6 個 RC 版本之后&#xff0c;Vue Router v4 閃亮…

實戰Nginx與PHP(FastCGI)的安裝、配置與優化

轉載鏈接&#xff1a;http://ixdba.blog.51cto.com/2895551/806622 一、什么是 FastCGI FastCGI是一個可伸縮地、高速地在HTTP server和動態腳本語言間通信的接口。多數流行的HTTP server都支持FastCGI&#xff0c;包括Apache、Nginx和lighttpd等&#xff0c;同時&#xff0c;…

python在運維自動化的前景_現在學運維自動化python和大數據?

{"moduleinfo":{"card_count":[{"count_phone":1,"count":1}],"search_count":[{"count_phone":7,"count":7}]},"card":[{"des":"阿里云實時計算(Alibaba Cloud Realtime Com…

BOM算最尾階的損耗率 成品直接到料件

假設由B生產為A經過3道工序,各工序的損耗率分別為 C1,C2,C3; 由D生產為B經過1道工序,作業損耗率為C4. 請問在BOM中建立材料的損耗率應該是怎樣的呀? 我的理解是這樣:A的產出B的投入(1-C1)(1-C2)(1-C3)所以B的投入A的產出/(1-C1)(1-C2)(1-C3)所以建A的BOM時,材料B的損耗率為: …

10個前端8個用Vue的,怎么才能在面試中出彩?

大家好&#xff0c;我是若川。現在但凡出去面試&#xff0c;面試官幾乎必問 Vue3.0 。不僅會問一些核心特性&#xff0c;還會問原理層面的問題。比如&#xff1a;?框架層面問題&#xff1a;Vue3.0 新特性 Composition API 與 React.js 中 Hooks 的異同點&#xff1f;?源碼、原…

ASP.NET MVC學習之(5):Html.ActionLink

本文整理了該方法的幾種重載形式&#xff1a; 一 Html.ActionLink("linkText","actionName") 該重載的第一個參數是該鏈接要顯示的文字&#xff0c;第二個參數是對應的控制器的方法&#xff0c;默認控制器為當前頁面的控制器&#xff0c;如果當前頁面的控制…

python qq模塊_常用的Python模塊

目錄1、使用copy模塊來復制>>> class Animal:def _init_(self, species, number_of_legs, color):self.species speciesself.number_of_legs number_of_legsself.color color>>> harry Animal()>>> harry._init_(hippogriff, 6, pink)>>&…

故鄉 | 登高望遠,夜幕降臨

歡迎星標我的公眾號若川視野&#xff0c;回復加群&#xff0c;長期交流學習上周末看了幾集豆瓣評分8.5分劉同同名小說的青春劇《我在未來等你》&#xff0c;讓我回想起自己的高中生活。也想起小時候經常爬到故鄉附近的小山&#xff0c;看夕陽西下。時常和同事開玩笑說&#xff…

CentOS5安裝Nginx1.4+PHP5.5 FastCGI

轉載鏈接&#xff1a;http://blog.csdn.net/staricqxyz/article/details/17012329 yum -y install gcc gcc-c autoconf libjpeg libjpeg-devel libpng libpng-devel freetype freetype-devel libxml2 libxml2-devel zlib zlib-devel glibc glibc-devel glib2 glib2-devel bzip2…

FTP服務器的搭建

IIS IIS所提供的FTP功能比較簡單&#xff1a; 用戶依賴于“操作系統用戶”&#xff1b;只提供了全局讀&#xff08;瀏覽和復制&#xff09;、寫&#xff08;刪除、修改、添加&#xff09;功能設置&#xff0c;也就是說所有的讀寫權限都相同&#xff1b;“用戶”與“對應目錄”的…

一份熱乎乎的滴滴前端面經

關注若川視野&#xff0c;回復"pdf" 領取資料&#xff0c;回復"加群"&#xff0c;可加群長期交流學習滴滴前端實習面經滴滴是我投簡歷之后第二家面試的公司&#xff0c; 國慶節前兩三天投的簡歷&#xff0c; 國慶后復工第一天就給我打了電話約一面。那時候…

用webBrowser取源文件取不到的點擊數--選秀榜selectop.com網站內容管理系統之六

用idhttp可以取到源文件&#xff0c;但網站用腳本代碼&#xff0c;源文件是看不到&#xff0c;并且代碼的結果也取不出。webBrowser可以多次返回下載到的內容&#xff0c;不包括任何html語法&#xff0c;這個當中就有文章的點擊數。在WebBrowser1DownloadComplete事件中處理&am…

Nginx負載均衡配置

轉載鏈接&#xff1a;http://blog.csdn.net/staricqxyz/article/details/16984029 將域名指向Nginx服務器 訪問www.test.com會轉發到192.168.1.22,192.168.1.23 user nobody nobody; worker_processes 1; events { worker_connections 1024; } http { …

linux查看python環境變量_Linux中添加PYTHONPATH配置anaconda環境變量方法

因為最近開發多智能體模型需要把自己寫的環境打包import&#xff0c;環境是統一的&#xff0c;如果不加入環境變量&#xff0c;每次測一個算法都要把包作為附屬腳本和算法腳本放一起非常麻煩。所以就想把這些寫的環境加入到python的環境變量里&#xff0c;這樣就不用每次測試都…

yii_wiki_145_yii-cjuidialog-for-create-new-model (通過CJuiDialog來創建新的Model)

/**** CJuiDialog for create new model http://www.yiiframework.com/wiki/145/cjuidialog-for-create-new-model/translated by php攻城師http://blog.csdn.net/phpgcsIntroduction Scenario Preparation of the form Enhance the action create The dialog Summary ***/Intr…