• 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

  • 相关阅读:
    Android App 注射&&Drozer Use
    Burp Suite使用介绍总结
    php截取后台登陆密码的代码
    通用型正方教务(通杀各版本)存在注入(不需登陆)+获得webshell+提权内网漫游
    密码重置漏洞案例
    新闻发布系统<分页>
    通过端口 8080 连接到主机 localhost 的 TCP/IP 连接失败
    九大内置对象
    jsp前3章试题分析
    富文本编辑器
  • 原文地址:https://www.cnblogs.com/xiaotie/p/1694278.html
Copyright © 2020-2023  润新知