C#模板編程(1):有了泛型,為什麼還需要模板?

來源:互聯網
上載者:User

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

 

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

(1)類型約束問題。

C#泛型的類型約束是個很嚴重的問題。

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

這樣的泛型方法無法直接實現。因為Byte,Int32等等並沒有公用介面。

沒有公用介面,但又想藉助泛型來節省代碼,減少維護量。怎麼辦呢?

我之前寫過2篇部落格《實現.net下的動態代理》、《實現.net下的動態代理(續)-多個物件Mixin》,通過Ducking Typing來實現,但這種實現方式不自然,且效能低下,只適合對效能不敏感的情境。

(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;
        }
    }
}

 

是不是很像模板編程呢?

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.