最近頻繁的接觸到泛型方面的問題,所以也不得不來學(xué)習(xí)一下了,開始主要是在msdn的webcast上去下載教學(xué)錄象看的(李建忠老師的),本篇文章先介紹一下泛型入門方面的知識,希望能讓剛開始學(xué)習(xí)泛型的朋友能夠更快的入門,言歸正傳,首先我們來看一下泛型的基本概念。
最顯著的一點就是它參數(shù)化了類型,把類型作為參數(shù)抽象出來,從而使我們在實際的運用當(dāng)中能夠更好的實現(xiàn)代碼的重復(fù)利用,同時它提供了更強的類型安全,更高的效率,不過在約束方面,它只支持顯示的約束,這樣在靈活性方面就顯得不是那么好了。我覺得它之所以能夠提供更高的效率是因為泛型在實例化的時候采用了"on-demand"的模式,即按需實例化,發(fā)生在jit(just in time)編譯時。
下面來看如何定義一個泛型類,很簡單,你只需要意識到一點,在這里,類型已經(jīng)被參數(shù)化了:
using system;
using system.collections.generic;
using system.text;
namespace generictest
{
class program
{
static void main(string[] args)
{
//使用string,int來實例化test<t,s>類
test<string, int> t = new test<string, int>("shy520",22);
//調(diào)用泛型類中的方法
t.setvalue();
}
}
/**//// <summary>
/// 定義一個泛型類,該類有兩個類型參數(shù),分別是t,s
/// http://pw.cnblogs.com
/// </summary>
/// <typeparam name="t">類型參數(shù)</typeparam>
/// <typeparam name="s">類型參數(shù)</typeparam>
public class test<t,s>
{
//泛型類的類型參數(shù)可用于類成員
private t name;
private s age;
public test(t name,s age)
{
this.name = name;
this.age = age;
}
public void setvalue()
{
console.writeline(name.tostring());
console.writeline(age.tostring());
}
}
}
上面的例子不是很恰當(dāng),目的是讓初學(xué)泛型的你了解一下泛型的定義及實例化方法,如上,我們定義了一個泛型類,那么如何實現(xiàn)泛型類的繼承呢?這里需要滿足下面兩點中的任何一點即可:
1、泛型類繼承中,父類的類型參數(shù)已被實例化,這種情況下子類不一定必須是泛型類;
2、父類的類型參數(shù)沒有被實例化,但來源于子類,也就是說父類和子類都是泛型類,并且二者有相同的類型參數(shù);
//如果這樣寫的話,顯然會報找不到類型t,s的錯誤
public class testchild : test<t, s> { }
//正確的寫法應(yīng)該是
public class testchild : test<string, int>{ }
public class testchild<t, s> : test<t, s> { }
public class testchild<t, s> : test<string, int> { }
接著我們來看看泛型接口,其創(chuàng)建以及繼承規(guī)則和上面說的泛型類是一樣的,看下面的代碼:
public interface ilist<t>
{
t[] getelements();
}
public interface idictionary<k,v>
{
void add(k key, v value);
}
// 泛型接口的類型參數(shù)要么已實例化
// 要么來源于實現(xiàn)類聲明的類型參數(shù)
class list<t> : ilist<t>, idictionary<int, t>
{
public t[] getelements() { return null; }
public void add(int index, t value)
{}
}
在來看一下泛型委托,首先我們定義一個類型參數(shù)為t的委托,然后在類中利用委托調(diào)用方法:
using system;
using system.collections.generic;
using system.text;
namespace generictest
{
//定義一個委托,類型參數(shù)為t,返回值類型t
//泛型委托支持在返回值和參數(shù)上應(yīng)用類型參數(shù)
delegate string genericdelete<t>(t value);
class test
{
static string f(int i) { return "shy520"; }
static string g(string s) { return "shy520"; }
static void main(string[] args)
{
genericdelete<string> g1 = g;
genericdelete<int> g2 = new genericdelete<int>(f);
}
}
}
我們再來看泛型方法,c#的泛型機制只支持在方法申明上包含類型參數(shù),也即是泛型方法。特別注意的是,泛型不支持在除了方法以外的其他類/接口成員上使用類型參數(shù),但這些成員可以被包含在泛型類型中,并且可以使用泛型類型的類型參數(shù)。還有一點需要說的就是,泛型方法可以在泛型類型中,也可以存在于非泛型類型中。下面我們分別看一下泛型類型的申明,調(diào)用,重載和覆蓋。
using system;
using system.collections.generic;
using system.text;
namespace generictest
{
class genericclass
{
//申明一個泛型方法
public t getvalue<t>(t t)
{
return t;
}
//調(diào)用泛型方法
//注意:在調(diào)用泛型方法時,對泛型方法的類型參數(shù)實例化
public int usemethod()
{
return this.getvalue<int>(10);
}
//重載getvalue方法
public int getvalue(int i)
{
return i;
}
}
//下面演示覆蓋
//要注意的是,泛型方法被覆蓋時,約束被默認(rèn)繼承,不需要重新指定約束關(guān)系
abstract class parent
{
public abstract k test<k, v>(k k, v v) where k : v;
}
class child : parent
{
public override t test<t, s>(t t, s s)
{
return t;
}
}
}
最后我們來看一下泛型中的約束:
c#中的泛型只支持顯示的約束,因為這樣才能保證c#所要求的類型安全,但顯示的約束并非時必須的,如果不加約束,泛型類型參數(shù)將只能訪問system.object類型中的公有方法。“顯式約束”由where子句表達,可以指定“基類約束”,“接口約束”,“構(gòu)造器約束”,“值類型/引用類型約束”共四種約束。下面的例子來源于李建忠老師的講座ppt。
1、基類約束:
class a { public void f1() {} }
class b { public void f2() {} }
class c<s,t>
where s: a // s繼承自a
where t: b // t繼承自b
{
// 可以在類型為s的變量上調(diào)用f1,
// 可以在類型為t的變量上調(diào)用f2
}
2、接口約束
interface iprintable { void print(); }
interface icomparable<t> { int compareto(t v);}
interface ikeyprovider<t> { t getkey(); }
class dictionary<k,v>
where k: icomparable<k>
where v: iprintable, ikeyprovider<k>
{
// 可以在類型為k的變量上調(diào)用compareto,
// 可以在類型為v的變量上調(diào)用print和getkey
}
3、構(gòu)造器約束
class a { public a() { } }
class b { public b(int i) { } }
class c<t>
where t : new()
{
//可以在其中使用t t=new t();
}
c<a> c=new c<a>(); //可以,a有無參構(gòu)造器
c<b> c=new c<b>(); //錯誤,b沒有無參構(gòu)造器
4、值/引用類型約束
public struct a { }
public class b { }
class c<t>
where t : struct
{
// t在這里面是一個值類型
}
c<a> c=new c<a>(); //可以,a是一個值類型
c<b> c=new c<b>(); //錯誤,b是一個引用類型