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() ...
随机推荐
- 无法将文件“..\bin\Debug \**.dll”复制到“bin\**.dll”。对路径“bin \**.dll”的访问被拒绝。
1.方法一: 将bin的只读属性去掉,就OK. 2.方法二: 直接关掉项目,重新打开.
- UWP: 妙用自定义 Action 以简化并重用代码
相信每一位 App 开发者,在开发过程中,都会有一些代码被反复用到,比如:复制文本,打电话,发短信,发邮件,给应用添加评论等等.在项目之间复制这些代码段,实在不是一个好办法,所以大家可能会把这些代码放 ...
- 【设计模式】不同设计模式体现IOC控制反转
使用过Spring的开发者应该都对IOC控制反转功能有所了解,最开始学习时应该都知道使用依赖注入来实现IOC的功能,本文来介绍使用IOC控制反转思想的几种设计模式. 依赖注入来实现IOC 注入依赖是I ...
- devtools进行热部署
热部署的形式这里只介绍一种devtools devtools可以实现页面热部署(即页面修改后会立即生效,这个可以直接在application.properties文件中配置spring.thymele ...
- Java基础篇——集合浅谈
原创不易,如需转载,请注明出处https://www.cnblogs.com/baixianlong/p/10703558.html,否则将追究法律责任!!! Set(基于Map来实现的,不细说) H ...
- python进程基础
目录 进程以及状态 1. 进程 2. 进程的状态 进程的创建-multiprocessing 1. 创建进程 2. 进程pid 3. Process语法结构如下 4. 给子进程指定的函数传递参数 5. ...
- mapper接口方法参数
mapper接口中的方法只有一个参数,是不影响程序员开发的可以将参数指定为 pojo类型 或 map
- CSS实现两列布局,一列固定宽度,一列宽度自适应方法
不管是左是右,反正就是一边宽度固定,一边宽度自适应. 博客园的很多主题也是这样设计的,我的博客也是右侧固定宽度,左侧自适应屏幕的布局方式. html代码: <div id="wrap& ...
- UOJ#310. 【UNR #2】黎明前的巧克力(FWT)
题意 题目链接 Sol 挂一个讲的看起来比较好的链接 然鹅我最后一步还是没看懂qwq.. 坐等SovietPower大佬发博客 #include<bits/stdc++.h> using ...
- 从项目需求角度,使用纯CSS方案解决垂直居中
CSS是HTML元素的剪刀手,它极度的丰富了web页面的修饰.在众多CSS常见的样式需求中,有一奇葩式的存在[垂直居中],因为不管是从逻辑实现方面还是从正常需求量来讲,这都没理由让这个需求在实践过程中 ...