根據 Assembly 生成代碼框架。
這是學習 AvalonEdit 的一個副產品。學習時,照著源代碼新建文件夾,新建文件,添加方法與屬性,雖然只是個框架,也要花費大量時間。為什麼不讓它自動生成呢?於是,新建了個控制台程序,一步步添加,一步步顯示,一步步調整。雖然還有許多不完善的地方,但它基本能用了。將 Main 方法略作改動,便成了 Build 方法。為操作方便,加了個 WPF 界面。OK!下一步可參照 ILSpy 來進行改寫,當也是一款不錯的工具吧。限於時間與能力,暫且作罷。
主要代碼如下:
1 /** 2 * Program.cs (c) 2015 by x01 3 */ 4 using System; 5 using System.Collections.Generic; 6 using System.Diagnostics; 7 using System.IO; 8 using System.Linq; 9 using System.Reflection; 10 using System.Text; 11 using System.Text.RegularExpressions; 12 using System.Windows; 13 14 namespace x01.CodeBuilder 15 { 16 public class Test<THllo> 17 { 18 19 } 20 /// <summary> 21 /// Description of Program. 22 /// </summary> 23 public static class BuildHelper 24 { 25 static readonly StringBuilder sb = new StringBuilder(); 26 static readonly List<string> usings = new List<string>(); 27 static string headerFormat = "/**\n * {0}.cs (c) 2015 by x01\n */\n"; 28 29 /// <summary> 30 /// 根據 Assebmly 生成代碼框架。 31 /// </summary> 32 /// <param name="path">程序集路徑名</param> 33 /// <param name="outputDirectory">輸出目錄</param> 34 public static void Build(string path, string outputDirectory) 35 { 36 if (!File.Exists(path)) { 37 throw new Exception("Assembly file not found."); 38 } 39 if (!Directory.Exists(outputDirectory)) { 40 Directory.CreateDirectory(outputDirectory); 41 } 42 43 Assembly assembly = Assembly.LoadFile(path); 44 string assemblyName = assembly.FullName.Split(',')[0]; 45 46 string assemblyPath = Path.GetDirectoryName(assembly.Location); 47 foreach (var type in assembly.GetTypes()) { 48 usings.Clear(); 49 50 if (!(type.IsEnum || type.IsClass || type.IsInterface || type.IsGenericType)) { 51 continue; 52 } 53 54 string typeName = type.FullName.Replace(assemblyName + ".",""); 55 56 string[] typeNameSplits = typeName.Split('.'); 57 string fileDirectory = outputDirectory; 58 if (typeNameSplits.Length > 1) { 59 for (int i = 0; i < typeNameSplits.Length - 1; i++) { 60 fileDirectory += "\\" + typeNameSplits[i]; 61 if (!Directory.Exists(fileDirectory)) { 62 Directory.CreateDirectory(fileDirectory); 63 } 64 } 65 } 66 67 sb.Clear(); 68 int lastDotIndex = type.FullName.LastIndexOf('.'); 69 string namespaceName = lastDotIndex > 0 ? type.FullName.Substring(0,lastDotIndex) : type.FullName; 70 71 sb.Append("namespace " + namespaceName + "\n{\n"); 72 73 string convertName = ConvertType(type.Name); 74 sb.Append("\t//TODO: " + convertName + "\n"); 75 76 if (type.IsPublic) { 77 sb.Append("\tpublic "); 78 } else { 79 sb.Append("\t"); 80 } 81 82 if (type.IsAbstract && !type.IsInterface) { 83 sb.Append("abstract "); 84 } else if (type.IsSealed && !type.IsEnum) { 85 sb.Append("sealed "); 86 } 87 88 if (type.IsEnum) { 89 sb.Append("enum "); 90 } else if (type.IsClass) { 91 sb.Append("class "); 92 } else if (type.IsInterface) { 93 sb.Append("interface "); 94 } 95 96 sb.Append(convertName); 97 if (type.BaseType != null && !string.IsNullOrEmpty(type.BaseType.Name)) { 98 sb.Append(" : " + ConvertType(type.BaseType.Name)); 99 } 100 sb.Append("\n\t{\n"); 101 102 var baseProperties = type.BaseType != null ? type.BaseType.GetProperties() : new PropertyInfo[1]; 103 foreach (var property in type.GetProperties()) { 104 if (type.IsEnum) continue; 105 106 var propertyString = property.ToString(); 107 bool skip = false; 108 foreach (var bp in baseProperties) { 109 if (bp == null) continue; 110 if (propertyString == bp.ToString()) { 111 skip = true; 112 break; 113 } 114 } 115 if (skip) continue; 116 117 int lastIndex = propertyString.LastIndexOf('.'); 118 string propu = string.Empty; 119 if (lastIndex >= 0) 120 propu = propertyString.Substring(0,lastIndex); 121 propu = ConvertType(propu); 122 if (!usings.Contains(propu) && !string.IsNullOrEmpty(propu)) { 123 usings.Add(propu); 124 } 125 string p = propertyString.Substring(lastIndex+1); 126 string[] ps = p.Split(' '); 127 ps[0] = ConvertType(ps[0]) + " "; 128 p = string.Empty; 129 for (int i = 0; i < ps.Length; i++) { 130 p += ps[i]; 131 } 132 sb.Append("\t\tpublic " + p + " { get; set; }\n"); 133 } 134 135 sb.Append("\n"); 136 137 var baseMethods = type.BaseType != null ? type.BaseType.GetMethods() : new MethodInfo[1]; 138 foreach (var method in type.GetMethods()) { 139 if (type.IsEnum) continue; 140 141 bool skip = false; 142 foreach (var bm in baseMethods) { 143 if (bm == null) break; 144 if (bm.ToString() == method.ToString()) { 145 skip = true; 146 break; 147 } 148 } 149 if (skip) continue; 150 151 var typeString = method.ReturnType.ToString(); 152 if (method.Name.Contains("get_") || method.Name.Contains("set_") 153 || method.Name.Contains("add_") || method.Name.Contains("remove_")) { 154 continue; 155 } 156 int lastIndex = typeString.LastIndexOf('.'); 157 string r = string.Empty; 158 if (lastIndex > 0) { 159 string u = typeString.Substring(0,lastIndex); 160 u = ConvertType(u); 161 if (!usings.Contains(u) && !string.IsNullOrEmpty(u)) { 162 usings.Add(u); 163 } 164 r = typeString.Substring(lastIndex+1); 165 } else { 166 r = typeString; 167 } 168 r = ConvertType(r); 169 sb.Append("\t\tpublic " + r + " " + method.Name + "("); 170 int pcount = method.GetParameters().Length; 171 int count = 0; 172 foreach (var parameter in method.GetParameters()) { 173 var paramString = parameter.ParameterType.ToString(); 174 int plast = paramString.LastIndexOf('.'); 175 string ptype = string.Empty; 176 if (plast > 0) { 177 string pu = paramString.Substring(0, plast); 178 pu = ConvertType(pu); 179 if (!usings.Contains(pu)) { 180 usings.Add(pu); 181 } 182 ptype = paramString.Substring(plast+1); 183 } else { 184 ptype = paramString; 185 } 186 ptype = ConvertType(ptype); 187 count ++; 188 if (pcount == 1 || pcount == count) 189 sb.Append(ptype + " " + parameter.Name); 190 else 191 sb.Append(ptype + " " + parameter.Name + ", "); 192 } 193 sb.Append(")\n"); 194 sb.Append("\t\t{\n\t\t\tthrow new NotImplementedException();\n\t\t}\n"); 195 } 196 197 sb.Append("\t}\n"); // end type 198 199 sb.Append("}"); //end namespace 200 201 string header = string.Format(headerFormat, convertName); 202 string ustring = header; 203 foreach (var us in usings) { 204 ustring += "using " + us + ";\n"; 205 } 206 ustring += "\n" + sb.ToString(); 207 208 if (!string.IsNullOrEmpty(convertName)) { 209 string filename = Path.Combine(fileDirectory, convertName) + ".cs"; 210 File.WriteAllText(filename, ustring); 211 } 212 } 213 } 214 215 static string ConvertType(string typeName) 216 { 217 int index = typeName.IndexOf('`'); 218 if (index >= 0) 219 typeName = typeName.Substring(0,index); 220 index = typeName.IndexOf('+'); 221 if (index >= 0) 222 typeName = typeName.Substring(0,index); 223 index = typeName.IndexOf('<'); 224 if (index >= 0) 225 typeName = typeName.Substring(0,index); 226 index = typeName.IndexOf(']'); 227 if (index >= 0) 228 typeName = typeName.Substring(0, index); 229 230 switch (typeName) { 231 case "Boolean": 232 return "bool"; 233 case "Void": 234 return "void"; 235 case "Int32": 236 return "int"; 237 case "Object": 238 return "object"; 239 case "Double": 240 return "double"; 241 case "String": 242 return "string"; 243 case "Long": 244 return "long"; 245 default: 246 break; 247 } 248 249 return typeName; 250 } 251 252 // 測試用的。 253 // static void Main(string[] args) 254 // { 255 // string path = @"E:\Temp\Lab\x01\x01.CodeBuilder\bin\Debug\x01.MelonEdit.exe"; 256 // string target = @"E:\Temp\Lab\x01\Output"; 257 // Build(path,target); 258 // 259 // Console.ReadKey(true); 260 // } 261 } 262 } View Code看一看,真是慘不忍睹。這大概就是所謂的意大利面條吧。
源代碼下載:https://github.com/chinax01/x01.CodeBuilder