C#编程の模板
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;
}
}
}
C#编程の模板的更多相关文章
- AJAX编程模板
AJAX一直以来没怎么接触,主要是做JSON数据在服务器和客户端之间传递的时候,被玩坏了,对它莫名的不可爱,最近心理阴影小了,于是又来看看它....... AJAX即“Asynchronous Jav ...
- C++编程模板2
C++编程模板2 #include <iostream> using namespace std; /* */ int main(){ int ans; printf("%d\n ...
- AutoCAD二次开发(2020版)--4,使用ARX向导创建编程模板(框架)--
手动创建ObjectARX应用程序非常麻烦,在此步骤中,将介绍ObjectARX向导. 在这里,我们将使用ObjectARX向导创建我们的ObjectARX应用程序. 本节的程序的需求是,接收CAD用 ...
- MapReduce 编程模板
1.MapReduce 编程模型的5个步骤: 1)迭代,将输入数据解析成 key/value 对: 2)将解析的 key/value经过Map处理映射成另一组key/value对: 3)根据key进行 ...
- SDK编程模板
#include<Windows.h> LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM); int WINAPI WinMain(HINS ...
- C++模板编程-模板基础重点
模板基础 1.模板参数自动推导,如果是已知的参数类型与个数,这调用模板时可以不写类型. Cout<<max<int>(1,3);可以写为Cout<<max(1,3) ...
- C++ Templates编程(模板参数)
//file max.hpp template <typename T> //template<class T> inline T const& max (T cons ...
- Java编程模板
package Campus; import java.util.Scanner; public class Main{ public static void main(String args[]){ ...
- Java Callable并发编程模板
submit方法会产生Future对象,它用Callable返回结果的特定类型进行了参数化,可以用isDone()的方法来查询Future是否已经完成.当任务完成是,它具有一个结果,可以调用get() ...
随机推荐
- SqlServer 技术点总结(持续更新)
本文是用于记录自己平时遇到的一些SQL问题或知识点,以便以后自己查阅,会持续的更新,增加内容.发在博客园也可以和各位博友共同学习交流,如文中记录的有错误之处希望指出,谢谢. 一.用SQL语句调用作业 ...
- mysql存储过程调用含out参数
mysql 数据库有以下存储过程: CREATE DEFINER=`root`@`localhost` PROCEDURE `hovertreeTest`( IN `Param1` INT, ), O ...
- [android] 服务的生命周期(混合方式)
绑定服务:可以调用服务里面的方法, 如果调用者activity销毁了,服务也会跟着销毁 单独解除绑定的时候,服务也会被销毁 开启服务:不可以调用服务里面的方法 如果调用者activity退出了,服务还 ...
- [android] 安卓进程优先级&为什么使用服务
应用程序:一组组件(activity service provider receiver)的集合 一般情况下,一个应用程序对应一个进程 一般情况下,关闭掉应用,关闭掉所有界面,应用程序的进程还在后台运 ...
- Android RecyclerView预览item
参考: Android Tools Attributes listItem 和 Sample Data 的用法 笔记 tools:text TextView可以实现预览,不影响实际的效果 例如: to ...
- Java多线程之wait、notify/notifyAll 详解,用wait 和notifyAll 以及synchronized实现阻塞队列,多线程拓展之ReentrantLock与Condition
前言:这几天看了很多关于多线程的知识,分享一波.(但是目前接触的项目还未用到过,最多用过线程池,想看线程池 请看我之前的博客) 关于基本的理论等 参考如下: https://www.cnblogs.c ...
- Java 控制类的引用类型,合理使用内存
Java提供了 java.lang.ref包,该包下的类均与垃圾回收机制相关 先介绍Java对象的集中引用类型 1.强引用 强引用是最常见的,创建对象就是强引用,如 String a = new St ...
- JavaScript中的let和const
在ES6之前,JavaScript中只有两种作用域:全局作用域和函数内部的局部作用域.ES6中新增了两个重要的关键字,let和const,从而引入了块级作用域. 关键字var 使用var关键字声明的变 ...
- [DOM基础]offsetHeight,clientHeight,scrollHeight,innerHeight,outerHeight等属性的解释
由于经常搞混这几个属性,所以查找资料总结一下,方便以后翻出来温习. 一.偏移量-以offset开头的 1.offsetHeight:元素在垂直方向上占用的空间大小,像素.包括元素的高度.可见的水平滚动 ...
- Java 去除字符串中的空白字符
通过String的trim()方法只能去掉字符串两端的空格字符,但是对于\t \n等其它空白字符确不能去掉,因此需通过正则表达式,将其中匹配到的空白字符去掉,代码如下: protected Strin ...