天天看點

C#模闆程式設計(1):有了泛型,為什麼還需要模闆?

C#泛型程式設計已經深入人心了。為什麼又提出C#模闆程式設計呢?因為C#泛型存在一些局限性,突破這些局限性,需要使用C#方式的模闆程式設計。由于C#文法、編譯器、IDE限制,C#模闆程式設計沒有C++模闆程式設計使用友善,但是,仍然可以解決一些問題。

下面先看C#泛型程式設計的兩個限制:

(1)類型限制問題。

C#泛型的類型限制是個很嚴重的問題。

假設需要寫一個泛型方法,這個方法有2個參數,然後方法傳回結果是這兩個參數的和。

這樣的泛型方法無法直接實作。因為Byte,Int32等等并沒有公共接口。

沒有公共接口,但又想借助泛型來節省代碼,減少維護量。怎麼辦呢?

(2)泛型指針問題。

泛型程式中無法使用泛型指針,因為編譯器編譯時無法知道具體類型的Size,這對寫unsafe代碼是很大的限制。

是以,我們需要C#模闆程式設計。C#模闆程式設計說起來很簡單,它借助的是C#的一個文法——Using T0=T1。它沒有C++那麼自然,因為它缺乏C/C++源檔案的Include機制。你可以将整塊的檔案在不同的類之間進行複制和粘帖。雖然複制和粘帖是一大邪惡,但總比複制/粘帖/替換要好很多。

下面是C#模闆程式設計的一個重要應用——圖像處理的空間線性濾波。關于空間線性濾波可參見各種圖像處理的書,這裡不做介紹。

我們假定圖像是一個泛型類Image<T0>,這裡T代表每一個像素的存儲類型。可以是Byte,Short,Int32,Int64,Single,Double等。然後,核是一個泛型類,Kernel<T1>,這裡還有第三個泛型類,就是用于存放中間資料的Image<T2>。為什麼需要T2呢?假如T0是Byte,T1是帶有Scale的Int32(如,Scale=9,每個元素值=1的3*3矩陣),計算的中間結果會超出Byte的最大值,需要一個不同類型緩存來存儲這個中間資料。

隻用泛型的話,解決不了這個問題。第一點,Byte,Short,Int32,Int64,Single,Double之間未實作共同接口;第二點,為提升性能,Image<T>采用非托管記憶體實作,使用指針進行操作,而泛型中無法使用泛型指針。

使用C#模闆可以解決這個問題——代碼照舊,隻是在頭部寫下:Using T0=XXX;Using T1=XXX;Using T2=XXX;即可。

由于欠缺源代碼的Include機制,當要編寫新的濾波器時,需要把相同的代碼複制過去,然後更改頭部的Using ……。但無論如何,這樣使用,還是比在代碼中直接寫明類型,然後複制、粘帖、替換新類型的可讀性以及可維護性要好。

如果.Net允許源代碼的Include,C#的模闆程式設計将變得更為流暢(比起C++還是欠缺很多)。不知道等後續.Net版本開放編譯服務之後,會不會有更優雅的寫法。

下面是我随便寫下的一段對圖像進行空間線性濾波的代碼(不要看具體算法,具體算法是極端錯誤的,且不完整的,隻用看看編碼風格就行了,寫這段代碼隻為驗證這種程式設計模式的優點和缺點):

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; 

        } 

    } 

}

是不是很像模闆程式設計呢?

本文轉自xiaotie部落格園部落格,原文連結http://www.cnblogs.com/xiaotie/archive/2010/03/22/1691705.html如需轉載請自行聯系原作者

xiaotie 集異璧實驗室(GEBLAB)

繼續閱讀