C#模板编程(1)

http://www.cnblogs.com/xiaotie/archive/2010/03/22/1691705.html

C#模板编程(1):有了泛型,为什么还需要模板?

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#中的泛型与C++中的模板

http://blog.csdn.net/newborn2012/article/details/12955199

今天一个同事问我C#中有没有模板函数,他想写一个函数能够处理不同的类型,里面算法一样,如果要重载实现的话,就造成大量重复的代码,而

且扩展性不好。我说肯定有啊,你可以上网搜一下,结果他说没搜到,而我对C#根本就不熟,我就和他说你先按照重载的方式先做着,我再查查。

晚上回家之后,拿了本C#的书翻了一下,一下就找到了,就是C#泛型,我猜同事肯定搜的”模板“,所以没搜到。下面记录一下C#的泛型和C++

中的模板不同之处:

1,声明方式

C#中的泛型声明方式为,在标识符后面直接加<T>即可,而C++需要使用template关键字,如下:

C#:

class Stack<T>
{
public void Push(T name);
......
};

C++:

template <class T>

class Stack
{
public:
void Push(T name);
};

从上面的两种写法可以看出,显然C#要更简单一些。

上面这是类的泛型化,对于函数的泛型,也类似,如下:

C#:

void Func<T>(T name);

C++:

template <class T>

void Func(T name);

2,泛型约束

C#的泛型比C++模板增加了一个泛型参数约束功能,这在某些场景下非常有用,它可以限制某个泛型参数的使用范围,例如我们可以约束泛型类

型Shop<T>的参数只能为Customer类或其子类。如果我们这么写:

class Customer
{
public string Name{get;set;};
public string CreditCardNo{get;set;};
}
class Shop<T>
{
public void Print(T customer)
{
var cust = (Customer)customer;
Console.WriteLine(cust.Name);
............
}
}

那么var cust = (Customer)customer;这句话将编译错误,C#无法进行类型转换,因为对于类型参数T,C#在编译期间根本知道它是什么类型,所

以无法进行转换,但是如果对T做个限制,结果会怎么样呢,先看看C#中泛型约束的语法:
class Shop<T> where T: Customer

很简单,就是增加一个where子句,约束的类型如果有多个可以用逗号隔开,如下:

class Shop<T> where T: class, ICustomer, new()

如果类型参数有多个,则写多个where,如下:

class Shop<T1, T2> where T1:Customer where T2:Superman

这里要注意的是C#的泛型约束是有很多要求的,约束类型只有6种,如下:

class:类型实参必须是引用类型;

struct:类型实参必须是值类型;

<base class name>:类型实参必须是指定的类或者其派生类;

<interface name>:类型实参必须是指定的接口类,或其派生类

new():类型实参必须有一个无参数的公共构造函数;

U:类型实参T必须是另一个类型实参U或者U的派生类。

而且where子句内部的约束类型是有顺序要求的,首先class,struct,<base class name>必须只能取其中一个,且只能放在最前面,之后是

<interface name>,这个可以是多个,而new()必须放在最后,向int,string这些 密封类则不能作为约束参数的。

所以上面的语句如果写成如下,就不会有错了,而且VS的IntelliSense会非常智能地弹出customer的成员,如下:

class Customer
{
public string Name{get;set;};
public string CreditCardNo{get;set;};
}
class Shop<T>
{
public void Print(T customer)
{
Console.WriteLine(customer.Name);
}
}

其他关于C++的模板和C#的泛型使用上基本一样,除了C#特有的一些功能,如委托泛型,接口泛型等。

对了,其实我同事那个问题用object做函数的参数也是可以实现的,但是执行效率比泛型要低。
========

C#设计模式(14)——模板方法模式(Template Method)

http://blog.jobbole.com/78088/

一、引言

提到模板,大家肯定不免想到生活中的“简历模板”、“论文模板”、“Word中模版文件”等,在现实生活中,模板的概念就是——有一个规定

的格式,然后每个人都可以根据自己的需求或情况去更新它,例如简历模板,下载下来的简历模板的格式都是相同的,然而我们下载下来简历模板

之后我们可以根据自己的情况填充不同的内容要完成属于自己的简历。在设计模式中,模板方法模式中模板和生活中模板概念非常类似,下面让我

们就详细介绍模板方法的定义,大家可以根据生活中模板的概念来理解模板方法的定义。

二、模板方法模式详细介绍

2.1 模板方法模式的定义

模板方法模式——在一个抽象类中定义一个操作中的算法骨架(对应于生活中的大家下载的模板),而将一些步骤延迟到子类中去实现(对应于我

们根据自己的情况向模板填充内容)。模板方法使得子类可以不改变一个算法的结构前提下,重新定义算法的某些特定步骤,模板方法模式把不变

行为搬到超类中,从而去除了子类中的重复代码。

2.2 模板方法模式的实现

理解了模板方法的定义之后,自然实现模板方法也不是什么难事了,下面以生活中炒蔬菜为例来实现下模板方法模式。在现实生活中,做蔬菜的步

骤都大致相同,如果我们针对每种蔬菜类定义一个烧的方法,这样在每个类中都有很多相同的代码,为了解决这个问题,我们一般的思路肯定是把

相同的部分抽象出来到抽象类中去定义,具体子类来实现具体的不同部分,这个思路也正式模板方法的实现精髓所在,具体实现代码如下:

C#

// 客户端调用
    class Client
    {
        static void Main(string[] args)
        {
            // 创建一个菠菜实例并调用模板方法
            Spinach spinach = new Spinach();
            spinach.CookVegetabel();
            Console.Read();
        }
    }

public abstract class Vegetabel
    {
        // 模板方法,不要把模版方法定义为Virtual或abstract方法,避免被子类重写,防止更改流程的执行顺序
        public  void CookVegetabel()
        {
            Console.WriteLine("抄蔬菜的一般做法");
            this.pourOil();
            this.HeatOil();
            this.pourVegetable();
            this.stir_fry();
        }

// 第一步倒油
        public  void pourOil()
        {
            Console.WriteLine("倒油");
        }

// 把油烧热
        public  void HeatOil()
        {
            Console.WriteLine("把油烧热");
        }

// 油热了之后倒蔬菜下去,具体哪种蔬菜由子类决定
        public abstract void pourVegetable();

// 开发翻炒蔬菜
        public  void stir_fry()
        {
            Console.WriteLine("翻炒");
        }
    }

// 菠菜
    public class Spinach : Vegetabel
    {
        public override void pourVegetable()
        {
            Console.WriteLine("倒菠菜进锅中");
        }
    }

// 大白菜
    public class ChineseCabbage : Vegetabel
    {      
        public override void pourVegetable()
        {
            Console.WriteLine("倒大白菜进锅中");
        }
    }

// 客户端调用
    class Client
    {
        static void Main(string[] args)
        {
            // 创建一个菠菜实例并调用模板方法
            Spinach spinach = new Spinach();
            spinach.CookVegetabel();
            Console.Read();
        }
    }
 
    public abstract class Vegetabel
    {
        // 模板方法,不要把模版方法定义为Virtual或abstract方法,避免被子类重写,防止更改流程的执行顺序
        public  void CookVegetabel()
        {
            Console.WriteLine("抄蔬菜的一般做法");
            this.pourOil();
            this.HeatOil();
            this.pourVegetable();
            this.stir_fry();
        }
 
        // 第一步倒油
        public  void pourOil()
        {
            Console.WriteLine("倒油");
        }
 
        // 把油烧热
        public  void HeatOil()
        {
            Console.WriteLine("把油烧热");
        }
 
        // 油热了之后倒蔬菜下去,具体哪种蔬菜由子类决定
        public abstract void pourVegetable();
 
        // 开发翻炒蔬菜
        public  void stir_fry()
        {
            Console.WriteLine("翻炒");
        }
    }
 
    // 菠菜
    public class Spinach : Vegetabel
    {
 
        public override void pourVegetable()
        {
            Console.WriteLine("倒菠菜进锅中");
        }
    }
 
    // 大白菜
    public class ChineseCabbage : Vegetabel
    {      
        public override void pourVegetable()
        {
            Console.WriteLine("倒大白菜进锅中");
        }
    }
在上面的实现中,具体子类中重写了导入蔬菜种类的方法,因为这个真是烧菜方法中不同的地方,所以由具体子类去实现它。

2.3 模板方法模式的类图

实现完模板方法模式之后,让我们看看模板方法的类图结构,以理清该模式中类之间的关系,具体类图如下:

模板方法模式中涉及了两个角色:

抽象模板角色(Vegetable扮演这个角色):定义了一个或多个抽象操作,以便让子类实现,这些抽象操作称为基本操作。
具体模板角色(ChineseCabbage和Spinach扮演这个角色):实现父类所定义的一个或多个抽象方法。
三、模板方法模式的优缺点

下面让我们继续分析下模板方法的优缺点。

优点:

实现了代码复用
能够灵活应对子步骤的变化,符合开放-封闭原则
缺点:因为引入了一个抽象类,如果具体实现过多的话,需要用户或开发人员需要花更多的时间去理清类之间的关系。

附:在.NET中模板方法的应用也很多,例如我们在开发自定义的Web控件或WinForm控件时,我们只需要重写某个控件的部分方法。

四、总结

到这里,模板方法的介绍就结束了,模板方法模式在抽象类中定义了算法的实现步骤,将这些步骤的实现延迟到具体子类中去实现,从而使所有子

类复用了父类的代码,所以模板方法模式是基于继承的一种实现代码复用的技术。
========

C++ 模板和 C# 泛型之间的区别(C# 编程指南)

https://msdn.microsoft.com/zh-cn/library/c6cyy67b(VS.80).aspx
Visual Studio 2005 其他版本 
C# 泛型和 C++ 模板都是用于提供参数化类型支持的语言功能。然而,这两者之间存在许多差异。在语法层面上,C# 泛型是实现参数化类型的更

简单方法,不具有 C++ 模板的复杂性。此外,C# 并不尝试提供 C++ 模板所提供的所有功能。在实现层面,主要区别在于,C# 泛型类型替换是

在运行时执行的,从而为实例化的对象保留了泛型类型信息。有关更多信息,请参见运行库中的泛型(C# 编程指南)。
以下是 C# 泛型和 C++ 模板之间的主要差异:
C# 泛型未提供与 C++ 模板相同程度的灵活性。例如,尽管在 C# 泛型类中可以调用用户定义的运算符,但不能调用算术运算符。
C# 不允许非类型模板参数,如 template C<int i> {}。
C# 不支持显式专用化,即特定类型的模板的自定义实现。
C# 不支持部分专用化:类型参数子集的自定义实现。
C# 不允许将类型参数用作泛型类型的基类。
C# 不允许类型参数具有默认类型。
在 C# 中,尽管构造类型可用作泛型,但泛型类型参数自身不能是泛型。C++ 确实允许模板参数。
C++ 允许那些可能并非对模板中的所有类型参数都有效的代码,然后将检查该代码中是否有用作类型参数的特定类型。C# 要求相应地编写类中的

代码,使之能够使用任何满足约束的类型。例如,可以在 C++ 中编写对类型参数的对象使用算术运算符 + 和 - 的函数,这会在使用不支持这些运

算符的类型来实例化模板时产生错误。C# 不允许这样;唯一允许的语言构造是那些可从约束推导出来的构造。
请参见
参考
泛型介绍(C# 编程指南)
概念
C# 编程指南
其他资源
Templates
========

C# 模板编程相关学习总结相关推荐

  1. golang辟谣之模板编程

    2019独角兽企业重金招聘Python工程师标准>>> 网上很多资料显示golang没有实现模板编程,学习吧为golang辟谣. 转载于:https://my.oschina.net ...

  2. 【D3D11游戏编程】学习笔记十八:模板缓冲区的使用、镜子的实现

    (注:[D3D11游戏编程]学习笔记系列由CSDN作者BonChoix所写,转载请注明出处:http://blog.csdn.net/BonChoix,谢谢~) 模板缓冲区(Stencil Buffe ...

  3. python网络编程需要学什么,python网络编程学习笔记(五):socket的一些补充 Python 网络编程需要学习哪些网络相关的知识...

    python的socket编程问题hdr = recvall(s, 5) if hdr is None: print 'Unexpected EOF receivingstruct在unpack的时候 ...

  4. 少儿编程Scratch学习教程--Scratch介绍及参赛相关

    之前写了几篇关于Scratch的使用文章.忽然想起,忘记介绍下Scratch了. Scratch是一款由麻省理工学院(MIT) 设计开发的少儿编程工具.官方网址是麻省理工学院网站的一个分支.这个软件的 ...

  5. 大量C语言、C++、C#、VC编程相关书籍下载

    大量C语言.C++.C#.VC编程相关书籍下载 http://www.juntuan.net/dh/CYYdowmload.htm C语言 C语言编程宝典大全 C语言实例教程 1.72 C语言程序基础 ...

  6. OpenGL编程入门学习

    OpenGL编程入门学习  非常详细的教程,很适合初学者 本文转自:http://www.cppblog.com/doing5552/archive/2009/01/08/71532.html === ...

  7. 【C++模板编程入门】模板介绍、模板定义、函数模板、类模板、模板的继承

    1.模块的引入 1.1.示例代码 #include <iostream> #include <string>using namespace std;//用template声明T ...

  8. 原创 OpenCV3编程入门 学习笔记(总)

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/qq_36163358/article/ ...

  9. OpenCV3编程入门 学习笔记(总)

    OpenCV3编程入门 学习笔记 2018.12.12-2018.12.29 此博客为在看过毛星云版<OpenCV3编程入门>后所总结的一本笔记,可供复习使用. 文章目录 OpenCV3编 ...

最新文章

  1. python遍历任意层次字典_Python递归中 return 代码陷阱
  2. mysql 热切换_热备服务器中,切换master中切换SQL
  3. 话里话外:“按单制造”的业务特点(一)
  4. Sys.WebForms.PageRequestManagerTimeoutException:服务器请求超时
  5. Number.parseInt(), Number.parseFloat()
  6. SQL Server使用convert对datetime日期数据进行获取
  7. NYOJ276 - 比较字母大小
  8. 数据库表设计必需元素_HTML5输入,必需,模式,数据列表
  9. 交易系统开发(六)——HFT高频交易
  10. 五种常见的linux系统有哪些,常见的Linux操作系统有哪些
  11. Unity 粒子特效 之 LogoEffect ParticleSystem 文字图片logo粒子特效
  12. java nginx 502_Nginx 中 502 和 504 错误详解
  13. 计算机辅助工程试卷,《计算机辅助工程软件(UG)》试卷10–2答.doc
  14. CS231n Module2: CNN part1:Architecture
  15. 灵飞经3 印神无双 第十四章 印神古墓 3
  16. Errors occurred during the build. Errors running builder 'JavaScript Validator'
  17. vgg16构造模型图(转)
  18. 局域网下,一台电脑通过另一台电脑上网
  19. 在wsl的图形化界面中无法启动没有管理权限的“synaptic软件包管理器”
  20. 2021.12- 参加第四届海淀区青少年科普科幻创作活动(一等奖)

热门文章

  1. c3p0 参数 模糊查询_mybatis之动态sql,模糊查询,结果集处理,mybatis分页及特殊字符处理...
  2. @EnableAspectJAutoProxy||AOP原理:【看给容器中注册了什么组件,这个组件什么时候工作,这个组件的功能是什么?】
  3. JDK源码解析 Integer类使用了享元模式
  4. java的知识点33——死锁及解决方案
  5. jquery的each()详细介绍
  6. Python 【微视】短视频的自动上传与发布实例演示,同时支持抖音、快手、哔哩哔哩、小红书、西瓜视频、微信视频号等平台的视频自动化同步发布
  7. BufferedReader和BufferedWriter读写文件
  8. nchoosek--二项式系数或所有的组合数
  9. oem718d 基准站设置_RTK基站设置、7参数、测点、放线等操作教程,文末有视频
  10. oracle游标错误,ORACLE游标与错误处理