C# 模板编程相关学习总结
生活随笔
收集整理的這篇文章主要介紹了
C# 模板编程相关学习总结
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
C#模板編程(1)
http://www.cnblogs.com/xiaotie/archive/2010/03/22/1691705.html
C#模板編程(1):有了泛型,為什么還需要模板?C#泛型編程已經(jīng)深入人心了。為什么又提出C#模板編程呢?因?yàn)镃#泛型存在一些局限性,突破這些局限性,需要使用C#方式的模板編程。由于
C#語(yǔ)法、編譯器、IDE限制,C#模板編程沒(méi)有C++模板編程使用方便,但是,仍然可以解決一些問(wèn)題。
下面先看C#泛型編程的兩個(gè)限制:
(1)類型約束問(wèn)題。
C#泛型的類型約束是個(gè)很嚴(yán)重的問(wèn)題。
假設(shè)需要寫一個(gè)泛型方法,這個(gè)方法有2個(gè)參數(shù),然后方法返回結(jié)果是這兩個(gè)參數(shù)的和。
這樣的泛型方法無(wú)法直接實(shí)現(xiàn)。因?yàn)锽yte,Int32等等并沒(méi)有公共接口。
沒(méi)有公共接口,但又想借助泛型來(lái)節(jié)省代碼,減少維護(hù)量。怎么辦呢?
我之前寫過(guò)2篇博客《實(shí)現(xiàn).net下的動(dòng)態(tài)代理》、《實(shí)現(xiàn).net下的動(dòng)態(tài)代理(續(xù))-多對(duì)象Mixin》,通過(guò)Ducking Typing來(lái)實(shí)現(xiàn),但這種實(shí)現(xiàn)方式不自
然,且性能低下,只適合對(duì)性能不敏感的場(chǎng)景。
(2)泛型指針問(wèn)題。
泛型程序中無(wú)法使用泛型指針,因?yàn)榫幾g器編譯時(shí)無(wú)法知道具體類型的Size,這對(duì)寫unsafe代碼是很大的限制。
因此,我們需要C#模板編程。C#模板編程說(shuō)起來(lái)很簡(jiǎn)單,它借助的是C#的一個(gè)語(yǔ)法——Using T0=T1。它沒(méi)有C++那么自然,因?yàn)樗狈/C
++源文件的Include機(jī)制。你可以將整塊的文件在不同的類之間進(jìn)行復(fù)制和粘帖。雖然復(fù)制和粘帖是一大邪惡,但總比復(fù)制/粘帖/替換要好很多。
下面是C#模板編程的一個(gè)重要應(yīng)用——圖像處理的空間線性濾波。關(guān)于空間線性濾波可參見各種圖像處理的書,這里不做介紹。
我們假定圖像是一個(gè)泛型類Image<T0>,這里T代表每一個(gè)像素的存儲(chǔ)類型。可以是Byte,Short,Int32,Int64,Single,Double等。然后,核
是一個(gè)泛型類,Kernel<T1>,這里還有第三個(gè)泛型類,就是用于存放中間數(shù)據(jù)的Image<T2>。為什么需要T2呢?假如T0是Byte,T1是帶有Scale
的Int32(如,Scale=9,每個(gè)元素值=1的3*3矩陣),計(jì)算的中間結(jié)果會(huì)超出Byte的最大值,需要一個(gè)不同類型緩存來(lái)存儲(chǔ)這個(gè)中間數(shù)據(jù)。
只用泛型的話,解決不了這個(gè)問(wèn)題。第一點(diǎn),Byte,Short,Int32,Int64,Single,Double之間未實(shí)現(xiàn)共同接口;第二點(diǎn),為提升性能,
Image<T>采用非托管內(nèi)存實(shí)現(xiàn),使用指針進(jìn)行操作,而泛型中無(wú)法使用泛型指針。
使用C#模板可以解決這個(gè)問(wèn)題——代碼照舊,只是在頭部寫下:Using T0=XXX;Using T1=XXX;Using T2=XXX;即可。
由于欠缺源代碼的Include機(jī)制,當(dāng)要編寫新的濾波器時(shí),需要把相同的代碼復(fù)制過(guò)去,然后更改頭部的Using ……。但無(wú)論如何,這樣使用,還是
比在代碼中直接寫明類型,然后復(fù)制、粘帖、替換新類型的可讀性以及可維護(hù)性要好。
如果.Net允許源代碼的Include,C#的模板編程將變得更為流暢(比起C++還是欠缺很多)。不知道等后續(xù).Net版本開放編譯服務(wù)之后,會(huì)不會(huì)有
更優(yōu)雅的寫法。
下面是我隨便寫下的一段對(duì)圖像進(jìn)行空間線性濾波的代碼(不要看具體算法,具體算法是極端錯(cuò)誤的,且不完整的,只用看看編碼風(fēng)格就行了,寫
這段代碼只為驗(yàn)證這種編程模式的優(yōu)點(diǎn)和缺點(diǎn)):
using System; using System.Collections.Generic; using System.Text;using T = System.Byte; using CacheT = System.Int32; using K = System.Int32;namespace Orc.SmartImage.UnmanagedImage { public static class FilterHelper { public unsafe static UnmanagedImage<T> Filter(this UnmanagedImage<T> src, FilterKernel<K> filter) { K* kernel = stackalloc K[filter.Length];Int32 srcWidth = src.Width; Int32 srcHeight = src.Height; Int32 kWidth = filter.Width; Int32 kHeight = filter.Height;T* start = (T*) src.StartIntPtr; T* lineStart = start; T* pStart = start; T* pTemStart = pStart; T* pT; Int32* pK;for (int c = 0; c < srcWidth; c++) { for (int r = 0; r < srcHeight; r++) { pTemStart = pStart; pK = kernel;Int32 val = 0; for (int kc = 0; kc < kWidth; kc++) { pT = pStart; for (int kr = 0; kr < kHeight; kr++) { val += *pK * *pT; pT++; pK++; }pStart += srcWidth; }pStart = pTemStart; pStart++; }lineStart += srcWidth; pStart = lineStart; } return null; } } }========
?C#中的泛型與C++中的模板
http://blog.csdn.net/newborn2012/article/details/12955199今天一個(gè)同事問(wèn)我C#中有沒(méi)有模板函數(shù),他想寫一個(gè)函數(shù)能夠處理不同的類型,里面算法一樣,如果要重載實(shí)現(xiàn)的話,就造成大量重復(fù)的代碼,而
且擴(kuò)展性不好。我說(shuō)肯定有啊,你可以上網(wǎng)搜一下,結(jié)果他說(shuō)沒(méi)搜到,而我對(duì)C#根本就不熟,我就和他說(shuō)你先按照重載的方式先做著,我再查查。
晚上回家之后,拿了本C#的書翻了一下,一下就找到了,就是C#泛型,我猜同事肯定搜的”模板“,所以沒(méi)搜到。下面記錄一下C#的泛型和C++
中的模板不同之處:
1,聲明方式
C#中的泛型聲明方式為,在標(biāo)識(shí)符后面直接加<T>即可,而C++需要使用template關(guān)鍵字,如下:
C#:
class Stack<T>
{
public void Push(T name);
......
};
C++:
template <class T>
class Stack
{
public:
void Push(T name);
};
從上面的兩種寫法可以看出,顯然C#要更簡(jiǎn)單一些。
上面這是類的泛型化,對(duì)于函數(shù)的泛型,也類似,如下:
C#:
void Func<T>(T name);
C++:
template <class T>
void Func(T name);
2,泛型約束
C#的泛型比C++模板增加了一個(gè)泛型參數(shù)約束功能,這在某些場(chǎng)景下非常有用,它可以限制某個(gè)泛型參數(shù)的使用范圍,例如我們可以約束泛型類
型Shop<T>的參數(shù)只能為Customer類或其子類。如果我們這么寫:
class Customer { public string Name{get;set;}; public string CreditCardNo{get;set;}; } class Shop<T> { public void Print(T customer) { var cust = (Customer)customer; Console.WriteLine(cust.Name); ............ } }
那么var cust = (Customer)customer;這句話將編譯錯(cuò)誤,C#無(wú)法進(jìn)行類型轉(zhuǎn)換,因?yàn)閷?duì)于類型參數(shù)T,C#在編譯期間根本知道它是什么類型,所
以無(wú)法進(jìn)行轉(zhuǎn)換,但是如果對(duì)T做個(gè)限制,結(jié)果會(huì)怎么樣呢,先看看C#中泛型約束的語(yǔ)法:
class Shop<T> where T: Customer
很簡(jiǎn)單,就是增加一個(gè)where子句,約束的類型如果有多個(gè)可以用逗號(hào)隔開,如下:
class Shop<T> where T: class, ICustomer, new()
如果類型參數(shù)有多個(gè),則寫多個(gè)where,如下:
class Shop<T1, T2> where T1:Customer where T2:Superman
這里要注意的是C#的泛型約束是有很多要求的,約束類型只有6種,如下:
class:類型實(shí)參必須是引用類型;
struct:類型實(shí)參必須是值類型;
<base class name>:類型實(shí)參必須是指定的類或者其派生類;
<interface name>:類型實(shí)參必須是指定的接口類,或其派生類
new():類型實(shí)參必須有一個(gè)無(wú)參數(shù)的公共構(gòu)造函數(shù);
U:類型實(shí)參T必須是另一個(gè)類型實(shí)參U或者U的派生類。
而且where子句內(nèi)部的約束類型是有順序要求的,首先class,struct,<base class name>必須只能取其中一個(gè),且只能放在最前面,之后是
<interface name>,這個(gè)可以是多個(gè),而new()必須放在最后,向int,string這些 密封類則不能作為約束參數(shù)的。
所以上面的語(yǔ)句如果寫成如下,就不會(huì)有錯(cuò)了,而且VS的IntelliSense會(huì)非常智能地彈出customer的成員,如下:
class Customer { public string Name{get;set;}; public string CreditCardNo{get;set;}; } class Shop<T> { public void Print(T customer) { Console.WriteLine(customer.Name); } }
其他關(guān)于C++的模板和C#的泛型使用上基本一樣,除了C#特有的一些功能,如委托泛型,接口泛型等。
對(duì)了,其實(shí)我同事那個(gè)問(wèn)題用object做函數(shù)的參數(shù)也是可以實(shí)現(xiàn)的,但是執(zhí)行效率比泛型要低。
========
C#設(shè)計(jì)模式(14)——模板方法模式(Template Method)
http://blog.jobbole.com/78088/一、引言
提到模板,大家肯定不免想到生活中的“簡(jiǎn)歷模板”、“論文模板”、“Word中模版文件”等,在現(xiàn)實(shí)生活中,模板的概念就是——有一個(gè)規(guī)定
的格式,然后每個(gè)人都可以根據(jù)自己的需求或情況去更新它,例如簡(jiǎn)歷模板,下載下來(lái)的簡(jiǎn)歷模板的格式都是相同的,然而我們下載下來(lái)簡(jiǎn)歷模板
之后我們可以根據(jù)自己的情況填充不同的內(nèi)容要完成屬于自己的簡(jiǎn)歷。在設(shè)計(jì)模式中,模板方法模式中模板和生活中模板概念非常類似,下面讓我
們就詳細(xì)介紹模板方法的定義,大家可以根據(jù)生活中模板的概念來(lái)理解模板方法的定義。
二、模板方法模式詳細(xì)介紹
2.1 模板方法模式的定義
模板方法模式——在一個(gè)抽象類中定義一個(gè)操作中的算法骨架(對(duì)應(yīng)于生活中的大家下載的模板),而將一些步驟延遲到子類中去實(shí)現(xiàn)(對(duì)應(yīng)于我
們根據(jù)自己的情況向模板填充內(nèi)容)。模板方法使得子類可以不改變一個(gè)算法的結(jié)構(gòu)前提下,重新定義算法的某些特定步驟,模板方法模式把不變
行為搬到超類中,從而去除了子類中的重復(fù)代碼。
2.2 模板方法模式的實(shí)現(xiàn)
理解了模板方法的定義之后,自然實(shí)現(xiàn)模板方法也不是什么難事了,下面以生活中炒蔬菜為例來(lái)實(shí)現(xiàn)下模板方法模式。在現(xiàn)實(shí)生活中,做蔬菜的步
驟都大致相同,如果我們針對(duì)每種蔬菜類定義一個(gè)燒的方法,這樣在每個(gè)類中都有很多相同的代碼,為了解決這個(gè)問(wèn)題,我們一般的思路肯定是把
相同的部分抽象出來(lái)到抽象類中去定義,具體子類來(lái)實(shí)現(xiàn)具體的不同部分,這個(gè)思路也正式模板方法的實(shí)現(xiàn)精髓所在,具體實(shí)現(xiàn)代碼如下:
C#
// 客戶端調(diào)用
? ? class Client
? ? {
? ? ? ? static void Main(string[] args)
? ? ? ? {
? ? ? ? ? ? // 創(chuàng)建一個(gè)菠菜實(shí)例并調(diào)用模板方法
? ? ? ? ? ? Spinach spinach = new Spinach();
? ? ? ? ? ? spinach.CookVegetabel();
? ? ? ? ? ? Console.Read();
? ? ? ? }
? ? }
? ? public abstract class Vegetabel
? ? {
? ? ? ? // 模板方法,不要把模版方法定義為Virtual或abstract方法,避免被子類重寫,防止更改流程的執(zhí)行順序
? ? ? ? public ?void CookVegetabel()
? ? ? ? {
? ? ? ? ? ? Console.WriteLine("抄蔬菜的一般做法");
? ? ? ? ? ? this.pourOil();
? ? ? ? ? ? this.HeatOil();
? ? ? ? ? ? this.pourVegetable();
? ? ? ? ? ? this.stir_fry();
? ? ? ? }
? ? ? ? // 第一步倒油
? ? ? ? public ?void pourOil()
? ? ? ? {
? ? ? ? ? ? Console.WriteLine("倒油");
? ? ? ? }
? ? ? ? // 把油燒熱
? ? ? ? public ?void HeatOil()
? ? ? ? {
? ? ? ? ? ? Console.WriteLine("把油燒熱");
? ? ? ? }
? ? ? ? // 油熱了之后倒蔬菜下去,具體哪種蔬菜由子類決定
? ? ? ? public abstract void pourVegetable();
? ? ? ? // 開發(fā)翻炒蔬菜
? ? ? ? public ?void stir_fry()
? ? ? ? {
? ? ? ? ? ? Console.WriteLine("翻炒");
? ? ? ? }
? ? }
? ? // 菠菜
? ? public class Spinach : Vegetabel
? ? {
? ? ? ? public override void pourVegetable()
? ? ? ? {
? ? ? ? ? ? Console.WriteLine("倒菠菜進(jìn)鍋中");
? ? ? ? }
? ? }
? ? // 大白菜
? ? public class ChineseCabbage : Vegetabel
? ? { ? ? ?
? ? ? ? public override void pourVegetable()
? ? ? ? {
? ? ? ? ? ? Console.WriteLine("倒大白菜進(jìn)鍋中");
? ? ? ? }
? ? }
// 客戶端調(diào)用
? ? class Client
? ? {
? ? ? ? static void Main(string[] args)
? ? ? ? {
? ? ? ? ? ? // 創(chuàng)建一個(gè)菠菜實(shí)例并調(diào)用模板方法
? ? ? ? ? ? Spinach spinach = new Spinach();
? ? ? ? ? ? spinach.CookVegetabel();
? ? ? ? ? ? Console.Read();
? ? ? ? }
? ? }
?
? ? public abstract class Vegetabel
? ? {
? ? ? ? // 模板方法,不要把模版方法定義為Virtual或abstract方法,避免被子類重寫,防止更改流程的執(zhí)行順序
? ? ? ? public ?void CookVegetabel()
? ? ? ? {
? ? ? ? ? ? Console.WriteLine("抄蔬菜的一般做法");
? ? ? ? ? ? this.pourOil();
? ? ? ? ? ? this.HeatOil();
? ? ? ? ? ? this.pourVegetable();
? ? ? ? ? ? this.stir_fry();
? ? ? ? }
?
? ? ? ? // 第一步倒油
? ? ? ? public ?void pourOil()
? ? ? ? {
? ? ? ? ? ? Console.WriteLine("倒油");
? ? ? ? }
?
? ? ? ? // 把油燒熱
? ? ? ? public ?void HeatOil()
? ? ? ? {
? ? ? ? ? ? Console.WriteLine("把油燒熱");
? ? ? ? }
?
? ? ? ? // 油熱了之后倒蔬菜下去,具體哪種蔬菜由子類決定
? ? ? ? public abstract void pourVegetable();
?
? ? ? ? // 開發(fā)翻炒蔬菜
? ? ? ? public ?void stir_fry()
? ? ? ? {
? ? ? ? ? ? Console.WriteLine("翻炒");
? ? ? ? }
? ? }
?
? ? // 菠菜
? ? public class Spinach : Vegetabel
? ? {
?
? ? ? ? public override void pourVegetable()
? ? ? ? {
? ? ? ? ? ? Console.WriteLine("倒菠菜進(jìn)鍋中");
? ? ? ? }
? ? }
?
? ? // 大白菜
? ? public class ChineseCabbage : Vegetabel
? ? { ? ? ?
? ? ? ? public override void pourVegetable()
? ? ? ? {
? ? ? ? ? ? Console.WriteLine("倒大白菜進(jìn)鍋中");
? ? ? ? }
? ? }
在上面的實(shí)現(xiàn)中,具體子類中重寫了導(dǎo)入蔬菜種類的方法,因?yàn)檫@個(gè)真是燒菜方法中不同的地方,所以由具體子類去實(shí)現(xiàn)它。
2.3 模板方法模式的類圖
實(shí)現(xiàn)完模板方法模式之后,讓我們看看模板方法的類圖結(jié)構(gòu),以理清該模式中類之間的關(guān)系,具體類圖如下:
模板方法模式中涉及了兩個(gè)角色:
抽象模板角色(Vegetable扮演這個(gè)角色):定義了一個(gè)或多個(gè)抽象操作,以便讓子類實(shí)現(xiàn),這些抽象操作稱為基本操作。
具體模板角色(ChineseCabbage和Spinach扮演這個(gè)角色):實(shí)現(xiàn)父類所定義的一個(gè)或多個(gè)抽象方法。
三、模板方法模式的優(yōu)缺點(diǎn)
下面讓我們繼續(xù)分析下模板方法的優(yōu)缺點(diǎn)。
優(yōu)點(diǎn):
實(shí)現(xiàn)了代碼復(fù)用
能夠靈活應(yīng)對(duì)子步驟的變化,符合開放-封閉原則
缺點(diǎn):因?yàn)橐肓艘粋€(gè)抽象類,如果具體實(shí)現(xiàn)過(guò)多的話,需要用戶或開發(fā)人員需要花更多的時(shí)間去理清類之間的關(guān)系。
附:在.NET中模板方法的應(yīng)用也很多,例如我們?cè)陂_發(fā)自定義的Web控件或WinForm控件時(shí),我們只需要重寫某個(gè)控件的部分方法。
四、總結(jié)
到這里,模板方法的介紹就結(jié)束了,模板方法模式在抽象類中定義了算法的實(shí)現(xiàn)步驟,將這些步驟的實(shí)現(xiàn)延遲到具體子類中去實(shí)現(xiàn),從而使所有子
類復(fù)用了父類的代碼,所以模板方法模式是基于繼承的一種實(shí)現(xiàn)代碼復(fù)用的技術(shù)。
========
C++ 模板和 C# 泛型之間的區(qū)別(C# 編程指南)
https://msdn.microsoft.com/zh-cn/library/c6cyy67b(VS.80).aspxVisual Studio 2005 其他版本?
C# 泛型和 C++ 模板都是用于提供參數(shù)化類型支持的語(yǔ)言功能。然而,這兩者之間存在許多差異。在語(yǔ)法層面上,C# 泛型是實(shí)現(xiàn)參數(shù)化類型的更
簡(jiǎn)單方法,不具有 C++ 模板的復(fù)雜性。此外,C# 并不嘗試提供 C++ 模板所提供的所有功能。在實(shí)現(xiàn)層面,主要區(qū)別在于,C# 泛型類型替換是
在運(yùn)行時(shí)執(zhí)行的,從而為實(shí)例化的對(duì)象保留了泛型類型信息。有關(guān)更多信息,請(qǐng)參見運(yùn)行庫(kù)中的泛型(C# 編程指南)。
以下是 C# 泛型和 C++ 模板之間的主要差異:
C# 泛型未提供與 C++ 模板相同程度的靈活性。例如,盡管在 C# 泛型類中可以調(diào)用用戶定義的運(yùn)算符,但不能調(diào)用算術(shù)運(yùn)算符。
C# 不允許非類型模板參數(shù),如 template C<int i> {}。
C# 不支持顯式專用化,即特定類型的模板的自定義實(shí)現(xiàn)。
C# 不支持部分專用化:類型參數(shù)子集的自定義實(shí)現(xiàn)。
C# 不允許將類型參數(shù)用作泛型類型的基類。
C# 不允許類型參數(shù)具有默認(rèn)類型。
在 C# 中,盡管構(gòu)造類型可用作泛型,但泛型類型參數(shù)自身不能是泛型。C++ 確實(shí)允許模板參數(shù)。
C++ 允許那些可能并非對(duì)模板中的所有類型參數(shù)都有效的代碼,然后將檢查該代碼中是否有用作類型參數(shù)的特定類型。C# 要求相應(yīng)地編寫類中的
代碼,使之能夠使用任何滿足約束的類型。例如,可以在 C++ 中編寫對(duì)類型參數(shù)的對(duì)象使用算術(shù)運(yùn)算符 + 和 - 的函數(shù),這會(huì)在使用不支持這些運(yùn)
算符的類型來(lái)實(shí)例化模板時(shí)產(chǎn)生錯(cuò)誤。C# 不允許這樣;唯一允許的語(yǔ)言構(gòu)造是那些可從約束推導(dǎo)出來(lái)的構(gòu)造。
請(qǐng)參見
參考
泛型介紹(C# 編程指南)
概念
C# 編程指南
其他資源
Templates
========
總結(jié)
以上是生活随笔為你收集整理的C# 模板编程相关学习总结的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 体感开发学习总结 - 二
- 下一篇: c# char unsigned_dll