zoukankan      html  css  js  c++  java
  • C#玩转指针(二):预处理器、using、partial关键字与region的妙用

    欲练神功,引刀自宫。为了避免内存管理的烦恼,Java咔嚓一下,把指针砍掉了。当年.Net也追随潮流,咔嚓了一下,化名小桂子,登堂入室进了皇宫。康熙往下面一抓:咦?还在?——原来是假太监韦小宝。

    打开unsafe选项,C#指针就biu的一下子蹦出来了。指针很强大,没必要抛弃这一强大的工具。诚然,在大多数情况下用不上指针,但在特定的情况下还是需要用到的。比如:

    (1)大规模的运算中使用指针来提高性能;

    (2)与非托管代码进行交互;

    (3)在实时程序中使用指针,自行管理内存和对象的生命周期,以减少GC的负担。

    目前使用指针的主要语言是C和C++。但是由于语法限制,C和C++中的指针的玩法很单调,在C#中,可以进行更优雅更好玩的玩法。本文是《重新认识C#: 玩转指针》一文的续篇,主要是对《重新认识C#: 玩转指针》内容进行总结和改进。

     

    C#下使用指针有两大限制:

    (1)使用指针只能操作struct,不能操作class;

    (2)不能在泛型类型代码中使用未定义类型的指针。

    第一个限制没办法突破,因此需要将指针操作的类型设为struct。struct + 指针,恩,就把C#当更好的C来用吧。对于第二个限制,写一个预处理器来解决问题。

     

    下面是我写的简单的C#预处理器的代码,不到200行:

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

    然后编译为 Csmacro.exe ,放入系统路径下。在需要使用预处理器的项目中添加 Pre-build event command lind:

    Csmacro.exe $(ProjectDir)

    Visual Studio 有个很好用的关键字 “region” ,我们就把它当作我们预处理器的关键字。include 一个文件的语法是:

    #region include "xxx.cs"
    #endregion

    一个文件中可以有多个 #region include 块。

    被引用的文件不能全部引用,因为一个C#文件中一般包含有 using,namespace … 等,全部引用的话会报编译错误。因此,在被引用文件中,需要通过关键字来规定被引用的内容:

    #region mixin

    #endregion

    这个预处理器比较简单。被引用的文件中只能存在一个 #region mixin 块,且在这个region的内部,不能有其它的region块。

    预处理器 Csmacro.exe 的作用就是找到所有 cs 文件中的 #region include 块,根据 #region include  路径找到被引用文件,将该文件中的 #region mixin 块 取出,替换进 #region include 块中,生成一个以_Csmacro.cs结尾的新文件 。

    由于C#的两个语法糖“partial” 和 “using”,预处理器非常好用。如果没有这两个语法糖,预处理器会很丑陋不堪。(谁说语法糖没价值!一些小小的语法糖,足以实现新的编程范式。)

    partial 关键字 可以保证一个类型的代码存在几个不同的源文件中,这保证了预处理器的执行,您可以像写正常的代码一样编写公共部分代码,并且正常编译。

    using 关键字可以为类型指定一个的别名。这是一个不起眼的语法糖,却在本文中非常重要:它可以为不同的类型指定一个相同的类型别名。之所以引入预处理器,就是为了复用包含指针的代码。我们可以将代码抽象成两部分:变化部分和不变部分。一般来说,变化部分是类型的型别,如果还有其它非类型的变化,我们也可以将这些变化封装成新的类型。这样一来,我们可以将变化的类型放在源文件的顶端,使用using 关键字,命名为固定的别名。然后把不变部分的代码,放在 #region mixin 块中。这样的话,让我们需要 #region include 时,只需要在 #region include  块的前面(需要在namespace {} 的外部)为类型别名指定新的类型。

    举例说明,位图根据像素的格式可以分为很多种,这里假设有两种图像,一种是像素是一个Byte的灰度图像ImageU8,一个是像素是一个Argb32的彩色图像ImageArgb32。ImageU8代码如下:

    代码
     1 public class ImageU8
     2 {
     3     public Int32 Width { getset; }
     4     public Int32 Height { getset; }
     5 
     6     public unsafe Byte* Pointer;
     7     public unsafe void SetValue(Int32 row, Int32 col, Byte value)
     8     {
     9         Pointer[row * Width + col] = value;
    10     }
    11 }

     在 ImageArgb32 中,我们也要写重复的代码:

    代码
     1 public class ImageArgb32
     2 {
     3     public Int32 Width { getset; }
     4     public Int32 Height { getset; }
     5 
     6     public unsafe Argb32* Pointer;
     7     public unsafe void SetValue(Int32 row, Int32 col, Argb32 value)
     8     {
     9         Pointer[row * Width + col] = value;
    10     }
    11 }

    对于 Width和Height属性,我们可以建立基类来进行抽象和复用,然而,对于m_pointer和SetValue方法,如果放在基类中,则需要抹去类型信息,且变的十分丑陋。由于C#不支持泛型类型的指针,也无法提取为泛型代码。

    使用 Csmacro.exe 预处理器,我们就可以很好的处理。

    首先,建立一个模板文件 Image_Template.cs 

    代码
     1 using TPixel = System.Byte; 
     2 
     3 using System; 
     4 
     5 namespace XXX.Hidden 
     6 
     7     class Image_Template 
     8     { 
     9         public Int32 Width { getset; } 
    10         public Int32 Height { getset; } 
    11 
    12         #region mixin 
    13 
    14         public unsafe TPixel* Pointer; 
    15         public unsafe void SetValue(Int32 row, Int32 col, TPixel value) 
    16         { 
    17             Pointer[row * Width + col] = value; 
    18         } 
    19 
    20         #endregion 
    21     } 
    22 }

    然后建立一个基类 BaseImage,再从BaseImage派生ImageU8和ImageArgb32。两个派生类都是 partial 类:

    代码
     1 using System; 
     2 using System.Collections.Generic; 
     3 using System.Text; 
     4 
     5 namespace XXX 
     6 
     7     public class BaseImage 
     8     { 
     9         public Int32 Width { getset; } 
    10         public Int32 Height { getset; } 
    11     } 
    12 
    13     public partial class ImageU8 : BaseImage 
    14     { 
    15     } 
    16 
    17     public partial class ImageArgb32 : BaseImage 
    18     { 
    19     } 
    20 }

    下面我们建立一个 ImageU8_ClassHelper.cs 文件,来 #region include 引用上面的模板文件:

     1 using TPixel = System.Byte; 
     2 
     3 using System; 
     4 namespace XXX 
     5 
     6     public partial class ImageU8 
     7     { 
     8         #region include "Image_Template.cs" 
     9         #endregion 
    10     } 
    11 }

    编译,编译器会自动生成文件 “ImageU8_ClassHelper_Csmacro.cs” 。将这个文件引入项目中,编译通过。这个文件内容是:

    代码
     1 using TPixel = System.Byte; 
     2 
     3 using System; 
     4 namespace XXX 
     5 
     6     public partial class ImageU8 
     7     {
     8 
     9         public unsafe TPixel* Pointer; 
    10         public unsafe void SetValue(Int32 row, Int32 col, TPixel value) 
    11         { 
    12             Pointer[row * Width + col] = value; 
    13         } 
    14 
    15     } 
    16 }

    对于 ImageArgb32 类也可以进行类似操作。

     

    从这个例子可以看出,使用 partial 关键字,能够让原代码、模板代码、ClassHelper代码三者共存。使用 using 关键字,可以分离出代码中变化的部分出来。

     

    下面是我写的图像操作的一些模板代码:

    (1)通过模板提供指针和索引器:

    代码
     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     public abstract class Image_Template : UnmanagedImage<TPixel> 
    12     { 
    13         private Image_Template() 
    14             : base(1,1
    15         { 
    16             throw new NotImplementedException(); 
    17         } 
    18 
    19         #region mixin 
    20 
    21         public unsafe TPixel* Start { get { return (TPixel*)this.StartIntPtr; } } 
    22 
    23         public unsafe TPixel this[int index] 
    24         { 
    25             get 
    26             { 
    27                 return Start[index]; 
    28             } 
    29             set 
    30             { 
    31                 Start[index] = value; 
    32             } 
    33         } 
    34 
    35         public unsafe TPixel this[int row, int col] 
    36         { 
    37             get 
    38             { 
    39                 return Start[row * this.Width + col]; 
    40             } 
    41             set 
    42             { 
    43                 Start[row * this.Width + col] = value; 
    44             } 
    45         } 
    46 
    47         public unsafe TPixel* Row(Int32 row) 
    48         { 
    49             if (row < 0 || row >= this.Height) throw new ArgumentOutOfRangeException("row"); 
    50             return Start + row * this.Width; 
    51         } 
    52 
    53         #endregion 
    54     } 
    55 }

    (2)通过模板提供常用的操作和Lambda表达式支持

    代码
      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 ImageClassHelper_Template 
     12     { 
     13         #region mixin 
     14 
     15         public unsafe delegate void ActionOnPixel(TPixel* p); 
     16         public unsafe delegate void ActionWithPosition(Int32 row, Int32 column, TPixel* p); 
     17         public unsafe delegate Boolean PredicateOnPixel(TPixel* p); 
     18 
     19         public unsafe static void ForEach(this UnmanagedImage<TPixel> src, ActionOnPixel handler) 
     20         { 
     21             TPixel* start = (TPixel*)src.StartIntPtr; 
     22             TPixel* end = start + src.Length; 
     23             while (start != end) 
     24             { 
     25                 handler(start); 
     26                 ++start; 
     27             } 
     28         } 
     29 
     30         public unsafe static void ForEach(this UnmanagedImage<TPixel> src, ActionWithPosition handler) 
     31         { 
     32             Int32 width = src.Width; 
     33             Int32 height = src.Height; 
     34 
     35             TPixel* p = (TPixel*)src.StartIntPtr; 
     36             for (Int32 r = 0; r < height; r++
     37             { 
     38                 for (Int32 w = 0; w < width; w++
     39                 { 
     40                     handler(w, r, p); 
     41                     p++
     42                 } 
     43             } 
     44         } 
     45 
     46         public unsafe static void ForEach(this UnmanagedImage<TPixel> src, TPixel* start, uint length, ActionOnPixel handler) 
     47         { 
     48             TPixel* end = start + src.Length; 
     49             while (start != end) 
     50             { 
     51                 handler(start); 
     52                 ++start; 
     53             } 
     54         } 
     55 
     56         public unsafe static Int32 Count(this UnmanagedImage<TPixel> src, PredicateOnPixel handler) 
     57         { 
     58             TPixel* start = (TPixel*)src.StartIntPtr; 
     59             TPixel* end = start + src.Length; 
     60             Int32 count = 0
     61             while (start != end) 
     62             { 
     63                 if (handler(start) == true) count++
     64                 ++start; 
     65             } 
     66             return count; 
     67         } 
     68 
     69         public unsafe static Int32 Count(this UnmanagedImage<TPixel> src, Predicate<TPixel> handler) 
     70         { 
     71             TPixel* start = (TPixel*)src.StartIntPtr; 
     72             TPixel* end = start + src.Length; 
     73             Int32 count = 0
     74             while (start != end) 
     75             { 
     76                 if (handler(*start) == true) count++
     77                 ++start; 
     78             } 
     79             return count; 
     80         } 
     81 
     82         public unsafe static List<TPixel> Where(this UnmanagedImage<TPixel> src, PredicateOnPixel handler) 
     83         { 
     84             List<TPixel> list = new List<TPixel>(); 
     85 
     86             TPixel* start = (TPixel*)src.StartIntPtr; 
     87             TPixel* end = start + src.Length; 
     88             while (start != end) 
     89             { 
     90                 if (handler(start) == true) list.Add(*start); 
     91                 ++start; 
     92             } 
     93 
     94             return list; 
     95         } 
     96 
     97         public unsafe static List<TPixel> Where(this UnmanagedImage<TPixel> src, Predicate<TPixel> handler) 
     98         { 
     99             List<TPixel> list = new List<TPixel>(); 
    100 
    101             TPixel* start = (TPixel*)src.StartIntPtr; 
    102             TPixel* end = start + src.Length; 
    103             while (start != end) 
    104             { 
    105                 if (handler(*start) == true) list.Add(*start); 
    106                 ++start; 
    107             } 
    108 
    109             return list; 
    110         } 
    111 
    112         /// <summary> 
    113         /// 查找模板。模板中值代表实际像素值。负数代表任何像素。返回查找得到的像素的左上端点的位置。 
    114         /// </summary> 
    115         /// <param name="template"></param> 
    116         /// <returns></returns> 
    117         public static unsafe List<System.Drawing.Point> FindTemplate(this UnmanagedImage<TPixel> src, int[,] template) 
    118         { 
    119             List<System.Drawing.Point> finds = new List<System.Drawing.Point>(); 
    120             int tHeight = template.GetUpperBound(0+ 1
    121             int tWidth = template.GetUpperBound(1+ 1
    122             int toWidth = src.Width - tWidth + 1
    123             int toHeight = src.Height - tHeight + 1
    124             int stride = src.Width; 
    125             TPixel* start = (TPixel*)src.SizeOfType; 
    126             for (int r = 0; r < toHeight; r++
    127             { 
    128                 for (int c = 0; c < toWidth; c++
    129                 { 
    130                     TPixel* srcStart = start + r * stride + c; 
    131                     for (int rr = 0; rr < tHeight; rr++
    132                     { 
    133                         for (int cc = 0; cc < tWidth; cc++
    134                         { 
    135                             int pattern = template[rr, cc]; 
    136                             if (pattern >= 0 && srcStart[rr * stride + cc] != pattern) 
    137                             { 
    138                                 goto Next; 
    139                             } 
    140                         } 
    141                     } 
    142 
    143                     finds.Add(new System.Drawing.Point(c, r)); 
    144 
    145                 Next: 
    146                     continue
    147                 } 
    148             } 
    149 
    150             return finds; 
    151         } 
    152 
    153         #endregion 
    154     } 
    155 }

    配合lambda表达式,用起来很爽。在方法“FindTemplate”中,有这一句:

    if (pattern >= 0 && srcStart[rr * stride + cc] != pattern)

    其中 srcStart[rr * stride + cc] 是 TPixel 不定类型,而 pattern 是 int 类型,两者之间需要进行比较,但是并不是所有的类型都提供和整数之间的 != 操作符。为此,我建立了个新的模板 TPixel_Template。

     

    (3)通过模板提供 != 操作符 的定义

    代码
     1 using TPixel = System.Byte; 
     2 using System; 
     3 
     4 namespace Orc.SmartImage.Hidden 
     5 
     6     public struct TPixel_Template 
     7     { 
     8         /* 
     9         #region mixin 
    10 
    11         public static Boolean operator ==(TPixel lhs, int rhs) 
    12         { 
    13             throw new NotImplementedException(); 
    14         } 
    15 
    16         public static Boolean operator !=(TPixel lhs, int rhs) 
    17         { 
    18             throw new NotImplementedException(); 
    19         } 
    20 
    21         public static Boolean operator ==(TPixel lhs, double rhs) 
    22         { 
    23             throw new NotImplementedException(); 
    24         } 
    25 
    26         public static Boolean operator !=(TPixel lhs, double rhs) 
    27         { 
    28             throw new NotImplementedException(); 
    29         } 
    30 
    31         public static Boolean operator ==(TPixel lhs, float rhs) 
    32         { 
    33             throw new NotImplementedException(); 
    34         } 
    35 
    36         public static Boolean operator !=(TPixel lhs, float rhs) 
    37         { 
    38             throw new NotImplementedException(); 
    39         } 
    40 
    41         #endregion 
    42 
    43         */ 
    44     } 
    45 }

    这里,在 #region mixin  块被注释掉了,不注释掉编译器会报错。注释之后,不会影响程序预处理。

     

    通过 ClassHelper类来使用模板:

    代码
     1 using System; 
     2 using System.Collections.Generic; 
     3 using System.Text; 
     4 
     5 namespace Orc.SmartImage 
     6 
     7     using TPixel = Argb32; 
     8     using TCache = System.Int32; 
     9     using TKernel = System.Int32; 
    10 
    11     public static partial class ImageArgb32ClassHelper 
    12     { 
    13         #region include "ImageClassHelper_Template.cs" 
    14         #endregion 
    15     } 
    16 
    17     public partial class ImageArgb32 
    18     { 
    19         #region include "Image_Template.cs" 
    20         #endregion 
    21     } 
    22 
    23     public partial struct Argb32 
    24     { 
    25         #region include "TPixel_Template.cs" 
    26         #endregion 
    27     } 
    28 

    由于 Argb32 未提供和 int 之间的比较,因此,在这里 #region include "TPixel_Template.cs"。而Byte可以与int比较,因此,在ImageU8中,就不需要#region include "TPixel_Template.cs": 

    代码
     3 using System; 
     4 using System.Collections.Generic; 
     5 using System.Text; 
     6 
     7 namespace Orc.SmartImage 
     8 
     9     using TPixel = System.Byte; 
    10     using TCache = System.Int32; 
    11     using TKernel = System.Int32; 
    12 
    13     public static partial class ImageU8ClassHelper 
    14     { 
    15         #region include "ImageClassHelper_Template.cs" 
    16         #endregion 
    17     } 
    18 
    19     public partial class ImageU8 
    20     { 
    21         #region include "Image_Template.cs" 
    22         #endregion 
    23     } 
    24 }

    是不是很有意思呢?强大的指针,结合C#强大的语法和快速编译,至少在图像处理这一块是很好用的。

     

  • 相关阅读:
    Linux vim编辑器
    Linux egrep及bash的变量
    Linux grep及正则表达式
    Linux bash及shell-script
    Linux 用户、组管理以及权限
    Linux 常用快捷键、管道及重定向
    Linux 文件系统
    Linux 系统基础
    Linux 基础命令
    Linux 命令帮助
  • 原文地址:https://www.cnblogs.com/xiaotie/p/1780440.html
Copyright © 2011-2022 走看看