C#编程の模板
C#泛型编程已经深⼊⼈⼼了。为什么⼜提出C#模板编程呢?因为C#泛型存在⼀些局限性,突破这些局限性,需要使⽤C#⽅式的模板编程。由于C#语法、编译器、IDE限制,C#模板编程没有C++模板编程使⽤⽅便,但是,仍然可以解决⼀些问题。
下⾯先看C#泛型编程的两个限制:
(1)类型约束问题。
简单好玩的编程代码复制C#泛型的类型约束是个很严重的问题。
假设需要写⼀个泛型⽅法,这个⽅法有2个参数,然后⽅法返回结果是这两个参数的和。
这样的泛型⽅法⽆法直接实现。因为Byte,Int32等等并没有公共接⼝。
没有公共接⼝,但⼜想借助泛型来节省代码,减少维护量。怎么办呢?
我之前写过2篇博客《》、《》,通过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;
}
}
}
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论