1, 什麼是泛型?
在理解泛型的定義之前,我們要明白非泛型的概念,非泛型就是大部分情況下是聲明一個類,然後封裝需要的行為,最後創建這些類的實例。
泛型是一種更准確地使用有一種以上的類型的代碼的方式。泛型允許我們聲明類型參數化的代碼,我們可以用不同的類型進行實例化。總結為一句話就是,泛型類型是類型的模板。
請仔細理解下面兩張圖,或許能更好地理解泛型的原理。
2, 泛型類?
創建和使用常規的,非泛型的類的過程有兩個步驟:聲明類和創建類的實例。
泛型的類不是實際的類,而是類的模板,所以我們必須先從它們構建實際的類類型, 然後個構建後的類類型的實例。
下圖演示了泛型類的創建過程:
3, 聲明泛型類?
聲明一個泛型類和聲明普通類差不多,主要有如下區別:
1> 在類名之後放置一組尖括號。
2> 在尖括號中用逗號分隔的占位符字符串來表示希望提供的類型。這叫類型參數(Type Parameter)
3> 在泛型類聲明的主體中使用類型參數來表示應該被替代的類型。
如下代碼所示聲明了一個SomeClass的泛型類:
www.2cto.com
class SomeClass<T1, T2> //聲明泛型類SomeClass,尖括號中是類型參數。
{
public T1 SomeVar = new T1(); //通常在這些位置使用類型。
}
注:泛型和非泛型區分的最主要的標志是:泛型類有尖括號。
4, 創建構造類型?
我們不能直接從泛型類型創建實例對象。首先,我們需要告訴編譯器使用哪些真實類型來替代占位符(類型參數)。編譯器獲取這些真實類型並從它創建一個真實類型對象。
創建構造類型例如:
SomeClass<short,int> //尖括號中為類型實參
5, 創建泛型類的變量和實例?
創建泛型類實例一般有兩種方法:
方法一:和普通非泛型類的對象創建方法相似
SomeClass<short, int> mySc1 = new SomeClass<short, int>(); //第一種方法創建泛型類實例。
方法二:使用Var關鍵字隱式創建:
var mySc2 = new SomeClass<short, int>(); //第二種方法,使用Var關鍵字創建。
例如:
class Program
{
static voidMain(string[] args)
{
var stackInt = new MyStack<int>(); //創建泛型類對象(或稱實例),創建構造類型和創建實例可以一步完成。
var stackString=new MyStack<String>();
stackInt.Push(3); //調用方法
stackInt.Push(5);
stackInt.Push(7);
stackInt.Print();
stackString.Push("Generics are great!");
stackString.Push("Hi there");
stackString.Print();
Console.ReadKey();
}
}
class MyStack<T> //聲明泛型類
{
T[] StackArray; //聲明數組
int StackPointer = 0; //聲明並初始化整形變量。
public void Push(T x) //定義無返回值方法
{
if (!IsStackFull)
{
StackArray[StackPointer++] = x; //為數組StackArray賦值。
}
}
public T Pop() //定義有返回值方法
{
return (!IsStackEmpty) //條件運算符,可以用if...else...語句等價表示。
? StackArray[--StackPointer]
: StackArray[0];
}
public MyStack() //構造函數初始化數組StackArray,為數組分配內存引用。
{
StackArray=new T[MaxStack];
}
public void Print() //構造函數
{
for (int i = StackPointer - 1; i >= 0; i--)
{
Console.WriteLine("Value:{0}",StackArray[i]);
}
}
const int MaxStack = 10; //聲明並初始化常量MaxStack
bool IsStackFull //聲明屬性
{
get { return StackPointer >= MaxStack; }
}
bool IsStackEmpty //聲明屬性
{
get { return StackPointer <= 0; }
}
}
程序輸出結果為:
附:泛型棧和非泛型棧之間的區別總結如下圖表:
非泛型
泛型
源代碼大小
更大:我們需要為每種類型進行一個新的實現。
更小:不管構造類型的數量多少,我們只需要一個實現。
可執行大小
無論每一個版本的棧是否會被使用,都會在編譯的版本中出現。
可執行文件中只會出現有構造類型的類型。
寫的難易度
易於書寫
比較難寫
維護的難易度
更容易出問題,因為所有修改需要應用到每一個可用的類型上。
易於維護,因為只需要修改一個地方。
6, 泛型結構?
例如:
struct PieceOfData<T> //聲明泛型結構
{
public PieceOfData(T value) //構造函數,初始化字段_Data。
{
_Data = value;
}
private T _Data;//聲明私有字段(只能在類的內部訪問)。
public T Data //聲明屬性
{
get { return _Data; }
set { _Data = value; }
}
}
class Program
{
static voidMain()
{
var intData=new PieceOfData<int>(10);//創建構造類型實例
var stringData=new PieceOfData<string>("Hi there!");
Console.WriteLine("intData={0}",intData.Data);//訪問屬性
Console.WriteLine("stringData={0}",stringData.Data);
Console.ReadKey();
}
}
程序輸出結果為:
7, 泛型接口?
泛型接口的聲明和非泛型接口的聲明差不多,但是需要在接口名稱之後的尖括號中有類型參數。
例如:
interface IMyIfc<T> //聲明泛型接口,尖括號中為類型參數。
{
T ReturnIt(T inValue); //聲明返回值類型為T的方法
}
class Simple<S> : IMyIfc<S> //聲明泛型類,實現了泛型接口。
{
public S ReturnIt(S inValue) //實現接口成員方法。
{
return inValue;
}
}
class Program
{
static voidMain(string[] args)
{
var trivInt = new Simple<int>(); //創建構造類型實例。尖括號中為類型實參。
var trivString = new Simple<string>();
Console.WriteLine("{0}",trivInt.ReturnIt(50)); //調用類對象實現的方法。
Console.WriteLine("{0}",trivString.ReturnIt("Hi there!"));
Console.ReadKey();
}
}
程序輸出結果為:
8, 泛型委托?
1> 要聲明泛型委托,在委托名稱之後,委托參數列表之前的尖括號中放類型參數列表。
2> 注意,在這裡泛型委托有兩個參數列表:委托形參列表和類型參數列表。
例如:
delegate void MyDelegate<T>(T value); //聲明泛型委托
class Simple
{
public static void PrintString(string s) //方法匹配委托,
{
Console.WriteLine(s);
}
public static void PrintUpperString(string s)
{
Console.WriteLine("{0}", s.ToUpper());//調用string的ToUpper方法實現將字符串轉換為大寫。
}
}
class Program
{
static voidMain(string[] args)
{
var MyDel = new MyDelegate<string>(Simple.PrintString); //創建委托的實例
MyDel += Simple.PrintUpperString; //為委托添加方法。
if (null != MyDel) //判斷委托是否為空。
{
MyDel("VinceInfo");//調用委托。
}
else
{
Console.WriteLine("Delegate is empty!");
}
Console.ReadKey();
}
}
程序輸出結果如下:
9, 泛型方法?
泛型方法和泛型委托相似,有兩個參數列表:
1> 封閉在圓括號內的方法參數列表。
2> 封閉在尖括號內的類型參數列表。
例如:
class Simple //非泛型類
{
static public void ReverseAndPrint<T>(T[] arr) //聲明泛型方法
{
Array.Reverse(arr);
foreach (T item in arr) //遍歷數組。使用類型參數T。
{
Console.Write("{0}",item.ToString());
Console.Write("");
}
Console.WriteLine(); //換行
}
}
class Program
{
static voidMain(string[] args)
{
//創建三種類型的數組。
var intArray = new int[] { 3,5,7,9,11};
var stringArray = new string[] { "first","second","third"};
var doubleArray = new double[] { 3.567,7.891,2.345};
Simple.ReverseAndPrint<int>(intArray); //調用泛型方法,顯示調用
Simple.ReverseAndPrint(intArray); //使用推斷類型隱式調用。
Simple.ReverseAndPrint<string>(stringArray);
Simple.ReverseAndPrint(stringArray);
Simple.ReverseAndPrint<double>(doubleArray);
Simple.ReverseAndPrint(doubleArray);
Console.ReadKey();
}
}
程序輸出結果為:
10,擴展方法和泛型類?
擴展方法可以和泛型類結合使用,它允許我們將類中的靜態方法關聯到不同的泛型類上,還允許我們像調用類構造實例的實例方法一樣來調用方法。
例如:
static class ExtendHolder
{
public static void Print<T>(this Holder<T> h) //聲明擴展方法並關聯到泛型類Holder<T>上。
{
T[] vals = h.GetValues(); //調用泛型類的方法。
Console.WriteLine("{0},\t{1},\t{2}",vals[0],vals[1],vals[2]); //"\t"轉義符
}
}
class Holder<T> //聲明泛型類
{
T[] vals=new T[3];//聲明並初始化數組。
public Holder(T v0, T v1, T v2) //構造函數,為數組賦值。
{
vals[0] = v0;
vals[1] = v1;
vals[2] = v2;
}
public T[] GetValues() //聲明方法,返回數組類型。
{
return vals;
}
}
class Program
{
static voidMain(string[] args)
{
var intHolder = new Holder<int>(3,5,7); //創建泛型類實例
var stringHolder = new Holder<string>("a1","b2","c3");
intHolder.Print(); //調用方法
stringHolder.Print();
Console.ReadKey();
}
}
程序輸出結果為:
關於泛型先寫到這裡,歡迎大家指正,謝謝
作者 永遠的麥子