zoukankan      html  css  js  c++  java
  • 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)程序,代码如下:

    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 == 0return;
     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 ,编译通过:

    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

    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,且编译通过:

    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

  • 相关阅读:
    PHP 开发 APP 接口 学习笔记与总结
    Java实现 LeetCode 43 字符串相乘
    Java实现 LeetCode 43 字符串相乘
    Java实现 LeetCode 43 字符串相乘
    Java实现 LeetCode 42 接雨水
    Java实现 LeetCode 42 接雨水
    Java实现 LeetCode 42 接雨水
    Java实现 LeetCode 41 缺失的第一个正数
    Java实现 LeetCode 41 缺失的第一个正数
    Java实现 LeetCode 41 缺失的第一个正数
  • 原文地址:https://www.cnblogs.com/xiaotie/p/1694278.html
Copyright © 2011-2022 走看看