C#模板编程(2): 编写C#预处理器,让模板来的再自然一点

在《C#模板编程(1):有了泛型,为什么还需要模板?》文中,指出了C#泛型的局限性,为了突破这个局限性,我们需要模板编程。但是,C#语法以及IDE均不支持C#模板编程,怎么办呢?自己动手,丰衣足食,编写自己的C#预处理器。

一、C#预处理机制设计

问题的关键就是在C#的源文件中引入include机制,设计下面的语法:

(1) 引入:#region include <path> #endregion

(2) 被引:#region mixin … #endgion

 

例子:假设A.cs需要引用B.cs中的代码。A文件内容为:

XXX

#region  include "B.cs"
#endregion

XXX

B.cs 文件内容为:

YYY

#region  mixin

MMM

#endregion

ZZZ

运行预处理器,对A文件进行处理,生成第三个文件A_.cs:

XXX

MMM

XXX

二、实现

编写预处理器:Csmacro.exe[Csmacro.zip](意思是CSharp Macro)程序,代码如下:

ExpandedBlockStart.gif Csmacro
  1  using  System;
  2  using  System.Collections.Generic;
  3  using  System.IO;
  4  using  System.Text;
  5  using  System.Text.RegularExpressions; 
  6 
  7  namespace  Orc.Util.Csmacro
  8  {
  9       class  Program
 10      {
 11           static  Regex includeReg  =   new  Regex( " #region\\s+include.+\\s+#endregion " );
 12           static  Regex mixinReg  =   new  Regex( " (?<=#region\\s+mixin\\s)[\\s|\\S]+(?=#endregion) " ); 
 13 
 14           ///   <summary>
 15           ///  Csmacro [dir|filePath]
 16           ///  
 17           ///  语法:
 18           ///      #region include ""
 19           ///      #endregion
 20           ///      
 21           ///   </summary>
 22           ///   <param name="args"></param>
 23           static   void  Main( string [] args)
 24          {
 25               if  (args.Length  !=   1 )
 26              {
 27                  PrintHelp();
 28                   return ;
 29              } 
 30 
 31              String filePath  =  args[ 0 ]; 
 32 
 33              Path.GetDirectoryName(filePath);
 34              String dirName  =  Path.GetDirectoryName(filePath);
 35  #if  DEBUG
 36              Console.WriteLine( " dir: "   +  dirName);
 37  #endif
 38              String fileName  =  Path.GetFileName(filePath);
 39  #if  DEBUG
 40              Console.WriteLine( " file: "   +  fileName);
 41  #endif  
 42 
 43               if  (String.IsNullOrEmpty(fileName))
 44              {
 45                  Csmacro( new  DirectoryInfo(dirName));
 46              }
 47               else
 48              {
 49                   if  (fileName.EndsWith( " .cs " ==   false )
 50                  {
 51                      Console.WriteLine( " Csmacro只能处理后缀为.cs的源程序. " );
 52                  }
 53                   else
 54                  {
 55                      Csmacro( new  FileInfo(fileName));
 56                  }
 57              } 
 58 
 59              Console.WriteLine( " [Csmacro]:处理完毕. " ); 
 60 
 61  #if  DEBUG
 62              Console.ReadKey();
 63  #endif
 64          } 
 65 
 66           static   void  Csmacro(DirectoryInfo di)
 67          {
 68              Console.WriteLine( " [Csmacro]:进入目录 "   +  di.FullName); 
 69 
 70               foreach  (FileInfo fi  in  di.GetFiles( " *.cs " , SearchOption.AllDirectories))
 71              {
 72                  Csmacro(fi);
 73              }
 74          } 
 75 
 76           static   void  Csmacro(FileInfo fi)
 77          {
 78              String fullName  =  fi.FullName;
 79               if  (fi.Exists  ==   false )
 80              {
 81                  Console.WriteLine( " [Csmacro]:文件不存在- "   +  fullName);
 82              }
 83               else   if  (fullName.EndsWith( " _Csmacro.cs " ))
 84              {
 85                   return ;
 86              }
 87               else
 88              {
 89                  String text  =  File.ReadAllText(fullName); 
 90 
 91                  DirectoryInfo parrentDirInfo  =  fi.Directory; 
 92 
 93                  MatchCollection mc  =  includeReg.Matches(text);
 94                   if  (mc  ==   null   ||  mc.Count  ==   0 return ;
 95                   else
 96                  {
 97                      Console.WriteLine( " [Csmacro]:处理文件 "   +  fullName); 
 98 
 99                      StringBuilder sb  =   new  StringBuilder();
100                      Match first  =  mc[ 0 ];
101                      Match last  =  mc[mc.Count  -   1 ]; 
102 
103                      sb.Append(text.Substring( 0 , first.Index)); 
104 
105                       foreach  (Match m  in  mc)
106                      {
107                          String tmp  =  Csmacro(parrentDirInfo, m.Value);
108                          sb.Append(tmp);
109                      } 
110 
111                      Int32 lastEnd  =  last.Index  +  last.Length;
112                       if  (lastEnd  <  text.Length)
113                      {
114                          sb.Append(text.Substring(lastEnd));
115                      }
116                      String newName  =  fullName.Substring( 0 , fullName.Length  -   3 +   " _Csmacro.cs " ;
117                       if  (File.Exists(newName))
118                      {
119                          Console.WriteLine( " [Csmacro]:删除旧文件 "   +  newName);
120                      }
121                      File.WriteAllText(newName, sb.ToString());
122                      Console.WriteLine( " [Csmacro]:生成文件 "   +  newName);
123                  }
124              }
125          } 
126 
127           static  String Csmacro(DirectoryInfo currentDirInfo, String text)
128          {
129              String outfilePath  =  text.Replace( " #region " , String.Empty).Replace( " #endregion " , String.Empty).Replace( " include " ,String.Empty).Replace( " \ "" ,String.Empty).Trim();
130               try
131              {
132                   if  (Path.IsPathRooted(outfilePath)  ==   false )
133                  {
134                      outfilePath  =  currentDirInfo.FullName  +   @" \ "   +  outfilePath;
135                  }
136                  FileInfo fi  =   new  FileInfo(outfilePath);
137                   if  (fi.Exists  ==   false )
138                  {
139                      Console.WriteLine( " [Csmacro]:文件 "   +  fi.FullName  +   " 不存在. " );
140                       return  text;
141                  }
142                   else
143                  {
144                       return  GetMixinCode(File.ReadAllText(fi.FullName));
145                  }
146              }
147               catch  (Exception ex)
148              {
149                  Console.WriteLine( " [Csmacro]:出现错误( "   +  outfilePath  +   " )- "   +  ex.Message);
150              }
151               finally
152              {
153              }
154               return  text;
155          } 
156 
157           static  String GetMixinCode(String txt)
158          {
159              Match m  =  mixinReg.Match(txt);
160               if  (m.Success  ==   true )
161              {
162                   return  m.Value;
163              }
164               else   return  String.Empty;
165          } 
166 
167           static   void  PrintHelp()
168          {
169              Console.WriteLine( " Csmacro [dir|filePath] " );
170          }
171      }
172  }
173 
174 

 

编译之后,放在系统路径下(或放入任一在系统路径下的目录)。然后,在VS的项目属性的Build Events的Pre-build event command line中写入“Csmacro.exe $(ProjectDir)”,即可在编译项目之前,对$(ProjectDir)目录下的所有cs程序进行预处理。

Csmacro.exe 对于包含#region include <path> #endregion代码的程序xx.cs,预处理生成名为 xx_Csmacro.cs的文件;对于文件名以"Csmacro.cs”结尾的文件,则不进行任何处理。

使用时要注意:

(1)#region include <path> 与 #endregion 之间不能有任何代码;

(2)#region mixin 与 #endgion 之间不能有其它的region

(3)不支持多级引用

三、示例

下面,以《C#模板编程(1):有了泛型,为什么还需要模板?》文尾的例子说明怎样编写C#模板程序:

(1)建立一个模板类 FilterHelper_Template.cs ,编译通过:

ExpandedBlockStart.gif FilterHelper_Template.cs
 1  using  TPixel  =  System.Byte; 
 2  using  TCache  =  System.Int32; 
 3  using  TKernel  =  System.Int32; 
 4 
 5  using  System; 
 6  using  System.Collections.Generic; 
 7  using  System.Text; 
 8 
 9  namespace  Orc.SmartImage.Hidden 
10 
11       static   class  FilterHelper_Template 
12      { 
13           #region  mixin 
14 
15           //  本算法是错误的,只为说明C#模板程序的使用。 
16           public   unsafe   static  UnmanagedImage < TPixel >  Filter( this  UnmanagedImage < TPixel >  src, FilterKernel < TKernel >  filter) 
17          { 
18              TKernel *  kernel  =   stackalloc  TKernel[filter.Length]; 
19 
20              Int32 srcWidth  =  src.Width; 
21              Int32 srcHeight  =  src.Height; 
22              Int32 kWidth  =  filter.Width; 
23              Int32 kHeight  =  filter.Height; 
24 
25              TPixel *  start  =  (TPixel * )src.StartIntPtr; 
26              TPixel *  lineStart  =  start; 
27              TPixel *  pStart  =  start; 
28              TPixel *  pTemStart  =  pStart; 
29              TPixel *  pT; 
30              TKernel *  pK; 
31 
32               for  ( int  c  =   0 ; c  <  srcWidth; c ++
33              { 
34                   for  ( int  r  =   0 ; r  <  srcHeight; r ++
35                  { 
36                      pTemStart  =  pStart; 
37                      pK  =  kernel; 
38 
39                      Int32 val  =   0
40                       for  ( int  kc  =   0 ; kc  <  kWidth; kc ++
41                      { 
42                          pT  =  pStart; 
43                           for  ( int  kr  =   0 ; kr  <  kHeight; kr ++
44                          { 
45                              val  +=   * pK  *   * pT; 
46                              pT ++
47                              pK ++
48                          } 
49 
50                          pStart  +=  srcWidth; 
51                      } 
52 
53                      pStart  =  pTemStart; 
54                      pStart ++
55                  } 
56 
57                  lineStart  +=  srcWidth; 
58                  pStart  =  lineStart; 
59              } 
60               return   null
61          } 
62           #endregion  
63      } 
64  }
65 
66 

 这里,我使用了命名空间Hidden,意思是这个命名空间不想让外部使用,因为它是模板类。

 

(2)编写实例化模板类 ImageU8FilterHelper.cs

ExpandedBlockStart.gif ImageU8FilterHelper.cs
 1  using  System; 
 2  using  System.Collections.Generic; 
 3  using  System.Text; 
 4 
 5  namespace  Orc.SmartImage 
 6 
 7       using  TPixel  =  System.Byte; 
 8       using  TCache  =  System.Int32; 
 9       using  TKernel  =  System.Int32; 
10 
11       public   static   partial   class  ImageU8FilterHelper 
12      { 
13           #region  include "FilterHelper_Template.cs" 
14           #endregion  
15      } 
16  }

注意:这里使用 partial class 是为了使代码与预处理器生成的代码共存,不产生编译错误。

 

(3)编译项目,可以发现,预处理器自动生成了代码文件ImageU8FilterHelper_Csmacro.cs,且编译通过:

ExpandedBlockStart.gif ImageU8FilterHelper_Csmacro.cs
 1  using  System; 
 2  using  System.Collections.Generic; 
 3  using  System.Text; 
 4 
 5  namespace  Orc.SmartImage 
 6 
 7       using  TPixel  =  System.Byte; 
 8       using  TCache  =  System.Int32; 
 9       using  TKernel  =  System.Int32; 
10 
11       public   static   partial   class  ImageU8FilterHelper 
12      {
13 
14           //  本算法是错误的,只为说明C#模板程序的使用。 
15           public   unsafe   static  UnmanagedImage < TPixel >  Filter( this  UnmanagedImage < TPixel >  src, FilterKernel < TKernel >  filter) 
16          { 
17              TKernel *  kernel  =   stackalloc  TKernel[filter.Length]; 
18 
19              Int32 srcWidth  =  src.Width; 
20              Int32 srcHeight  =  src.Height; 
21              Int32 kWidth  =  filter.Width; 
22              Int32 kHeight  =  filter.Height; 
23 
24              TPixel *  start  =  (TPixel * )src.StartIntPtr; 
25              TPixel *  lineStart  =  start; 
26              TPixel *  pStart  =  start; 
27              TPixel *  pTemStart  =  pStart; 
28              TPixel *  pT; 
29              TKernel *  pK; 
30 
31               for  ( int  c  =   0 ; c  <  srcWidth; c ++
32              { 
33                   for  ( int  r  =   0 ; r  <  srcHeight; r ++
34                  { 
35                      pTemStart  =  pStart; 
36                      pK  =  kernel; 
37 
38                      Int32 val  =   0
39                       for  ( int  kc  =   0 ; kc  <  kWidth; kc ++
40                      { 
41                          pT  =  pStart; 
42                           for  ( int  kr  =   0 ; kr  <  kHeight; kr ++
43                          { 
44                              val  +=   * pK  *   * pT; 
45                              pT ++
46                              pK ++
47                          } 
48 
49                          pStart  +=  srcWidth; 
50                      } 
51 
52                      pStart  =  pTemStart; 
53                      pStart ++
54                  } 
55 
56                  lineStart  +=  srcWidth; 
57                  pStart  =  lineStart; 
58              } 
59               return   null
60          } 
61      } 
62  }
63 
64 

四、小结

这样一来,C#模板类使用就方便了很多,不必手动去处理模板类的复制和粘帖。虽然仍没有C++模板使用那么自然,毕竟又近了一步。:P

转载于:https://www.cnblogs.com/xiaotie/archive/2010/03/25/1694278.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值