C#模板编程(2): 编写C#预处理器,让模板来的再自然一点
2010-09-30 22:42:25 来源:WEB开发网在《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 == 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
更多精彩
赞助商链接