CodeEmitter.cs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561
  1. using System;
  2. using System.Collections.Immutable;
  3. using System.Text;
  4. namespace Apq.Cfg.SourceGenerator;
  5. /// <summary>
  6. /// 代码生成器,生成配置绑定代码
  7. /// </summary>
  8. internal static class CodeEmitter
  9. {
  10. /// <summary>
  11. /// 生成配置类的绑定代码
  12. /// </summary>
  13. public static string EmitBinderClass(ConfigClassInfo classInfo)
  14. {
  15. var sb = new StringBuilder();
  16. sb.AppendLine("// <auto-generated/>");
  17. sb.AppendLine("// 此文件由 Apq.Cfg.SourceGenerator 自动生成,请勿手动修改");
  18. sb.AppendLine("#nullable enable");
  19. sb.AppendLine();
  20. // 命名空间
  21. if (!string.IsNullOrEmpty(classInfo.Namespace))
  22. {
  23. sb.AppendLine($"namespace {classInfo.Namespace}");
  24. sb.AppendLine("{");
  25. }
  26. // partial 类
  27. var indent = string.IsNullOrEmpty(classInfo.Namespace) ? "" : " ";
  28. sb.AppendLine($"{indent}partial class {classInfo.ClassName}");
  29. sb.AppendLine($"{indent}{{");
  30. // BindFrom 静态方法
  31. EmitBindFromMethod(sb, classInfo, indent + " ");
  32. // BindTo 实例方法(绑定到已有实例)
  33. EmitBindToMethod(sb, classInfo, indent + " ");
  34. sb.AppendLine($"{indent}}}");
  35. if (!string.IsNullOrEmpty(classInfo.Namespace))
  36. {
  37. sb.AppendLine("}");
  38. }
  39. return sb.ToString();
  40. }
  41. /// <summary>
  42. /// 生成 BindFrom 静态方法
  43. /// </summary>
  44. private static void EmitBindFromMethod(StringBuilder sb, ConfigClassInfo classInfo, string indent)
  45. {
  46. sb.AppendLine($"{indent}/// <summary>");
  47. sb.AppendLine($"{indent}/// 从配置节创建并绑定配置对象(零反射)");
  48. sb.AppendLine($"{indent}/// </summary>");
  49. sb.AppendLine($"{indent}/// <param name=\"section\">配置节</param>");
  50. sb.AppendLine($"{indent}/// <returns>绑定后的配置对象</returns>");
  51. sb.AppendLine($"{indent}[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]");
  52. sb.AppendLine($"{indent}public static {classInfo.ClassName} BindFrom(global::Apq.Cfg.ICfgSection section)");
  53. sb.AppendLine($"{indent}{{");
  54. sb.AppendLine($"{indent} if (section == null) throw new global::System.ArgumentNullException(nameof(section));");
  55. sb.AppendLine();
  56. sb.AppendLine($"{indent} var result = new {classInfo.ClassName}();");
  57. sb.AppendLine($"{indent} BindTo(section, result);");
  58. sb.AppendLine($"{indent} return result;");
  59. sb.AppendLine($"{indent}}}");
  60. sb.AppendLine();
  61. }
  62. /// <summary>
  63. /// 生成 BindTo 静态方法
  64. /// </summary>
  65. private static void EmitBindToMethod(StringBuilder sb, ConfigClassInfo classInfo, string indent)
  66. {
  67. sb.AppendLine($"{indent}/// <summary>");
  68. sb.AppendLine($"{indent}/// 将配置节绑定到已有对象(零反射)");
  69. sb.AppendLine($"{indent}/// </summary>");
  70. sb.AppendLine($"{indent}/// <param name=\"section\">配置节</param>");
  71. sb.AppendLine($"{indent}/// <param name=\"target\">目标对象</param>");
  72. sb.AppendLine($"{indent}[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]");
  73. sb.AppendLine($"{indent}public static void BindTo(global::Apq.Cfg.ICfgSection section, {classInfo.ClassName} target)");
  74. sb.AppendLine($"{indent}{{");
  75. sb.AppendLine($"{indent} if (section == null) throw new global::System.ArgumentNullException(nameof(section));");
  76. sb.AppendLine($"{indent} if (target == null) throw new global::System.ArgumentNullException(nameof(target));");
  77. sb.AppendLine();
  78. foreach (var prop in classInfo.Properties)
  79. {
  80. EmitPropertyBinding(sb, prop, indent + " ");
  81. }
  82. sb.AppendLine($"{indent}}}");
  83. }
  84. /// <summary>
  85. /// 生成单个属性的绑定代码
  86. /// </summary>
  87. private static void EmitPropertyBinding(StringBuilder sb, PropertyInfo prop, string indent)
  88. {
  89. switch (prop.TypeKind)
  90. {
  91. case TypeKind.Simple:
  92. EmitSimplePropertyBinding(sb, prop, indent);
  93. break;
  94. case TypeKind.Array:
  95. EmitArrayPropertyBinding(sb, prop, indent);
  96. break;
  97. case TypeKind.List:
  98. EmitListPropertyBinding(sb, prop, indent);
  99. break;
  100. case TypeKind.HashSet:
  101. EmitHashSetPropertyBinding(sb, prop, indent);
  102. break;
  103. case TypeKind.Dictionary:
  104. EmitDictionaryPropertyBinding(sb, prop, indent);
  105. break;
  106. case TypeKind.Complex:
  107. EmitComplexPropertyBinding(sb, prop, indent);
  108. break;
  109. default:
  110. sb.AppendLine($"{indent}// TODO: 不支持的类型 {prop.TypeName} for {prop.Name}");
  111. break;
  112. }
  113. }
  114. /// <summary>
  115. /// 生成简单类型属性绑定
  116. /// </summary>
  117. private static void EmitSimplePropertyBinding(StringBuilder sb, PropertyInfo prop, string indent)
  118. {
  119. sb.AppendLine($"{indent}// {prop.Name}: {prop.TypeName}");
  120. sb.AppendLine($"{indent}{{");
  121. sb.AppendLine($"{indent} var __value = section.Get(\"{prop.Name}\");");
  122. sb.AppendLine($"{indent} if (__value != null)");
  123. sb.AppendLine($"{indent} {{");
  124. // 根据类型生成转换代码
  125. var baseType = prop.TypeName.TrimEnd('?');
  126. var convertCode = GetConvertCode(prop.TypeName, "__value");
  127. // string 和 Uri 是引用类型,不需要 .Value
  128. if (baseType == "string" || baseType == "System.String")
  129. {
  130. sb.AppendLine($"{indent} target.{prop.Name} = {convertCode};");
  131. }
  132. else if (baseType == "System.Uri")
  133. {
  134. sb.AppendLine($"{indent} target.{prop.Name} = {convertCode};");
  135. }
  136. else if (prop.IsNullable)
  137. {
  138. sb.AppendLine($"{indent} target.{prop.Name} = {convertCode};");
  139. }
  140. else
  141. {
  142. sb.AppendLine($"{indent} var __converted = {convertCode};");
  143. sb.AppendLine($"{indent} if (__converted != null) target.{prop.Name} = __converted.Value;");
  144. }
  145. sb.AppendLine($"{indent} }}");
  146. sb.AppendLine($"{indent}}}");
  147. sb.AppendLine();
  148. }
  149. /// <summary>
  150. /// 生成数组属性绑定
  151. /// </summary>
  152. private static void EmitArrayPropertyBinding(StringBuilder sb, PropertyInfo prop, string indent)
  153. {
  154. var elementType = prop.ElementTypeName ?? "object";
  155. sb.AppendLine($"{indent}// {prop.Name}: {prop.TypeName}");
  156. sb.AppendLine($"{indent}{{");
  157. sb.AppendLine($"{indent} var __childSection = section.GetSection(\"{prop.Name}\");");
  158. sb.AppendLine($"{indent} var __keys = __childSection.GetChildKeys()");
  159. sb.AppendLine($"{indent} .Where(k => int.TryParse(k, out _))");
  160. sb.AppendLine($"{indent} .OrderBy(k => int.Parse(k))");
  161. sb.AppendLine($"{indent} .ToList();");
  162. sb.AppendLine($"{indent} if (__keys.Count > 0)");
  163. sb.AppendLine($"{indent} {{");
  164. sb.AppendLine($"{indent} var __array = new {elementType}[__keys.Count];");
  165. sb.AppendLine($"{indent} for (int __i = 0; __i < __keys.Count; __i++)");
  166. sb.AppendLine($"{indent} {{");
  167. EmitElementBinding(sb, elementType, "__childSection", "__keys[__i]", $"__array[__i]", indent + " ");
  168. sb.AppendLine($"{indent} }}");
  169. sb.AppendLine($"{indent} target.{prop.Name} = __array;");
  170. sb.AppendLine($"{indent} }}");
  171. sb.AppendLine($"{indent}}}");
  172. sb.AppendLine();
  173. }
  174. /// <summary>
  175. /// 生成 List 属性绑定
  176. /// </summary>
  177. private static void EmitListPropertyBinding(StringBuilder sb, PropertyInfo prop, string indent)
  178. {
  179. var elementType = prop.ElementTypeName ?? "object";
  180. sb.AppendLine($"{indent}// {prop.Name}: {prop.TypeName}");
  181. sb.AppendLine($"{indent}{{");
  182. sb.AppendLine($"{indent} var __childSection = section.GetSection(\"{prop.Name}\");");
  183. sb.AppendLine($"{indent} var __keys = __childSection.GetChildKeys()");
  184. sb.AppendLine($"{indent} .Where(k => int.TryParse(k, out _))");
  185. sb.AppendLine($"{indent} .OrderBy(k => int.Parse(k))");
  186. sb.AppendLine($"{indent} .ToList();");
  187. sb.AppendLine($"{indent} if (__keys.Count > 0)");
  188. sb.AppendLine($"{indent} {{");
  189. sb.AppendLine($"{indent} var __list = new global::System.Collections.Generic.List<{elementType}>(__keys.Count);");
  190. sb.AppendLine($"{indent} foreach (var __key in __keys)");
  191. sb.AppendLine($"{indent} {{");
  192. EmitElementBindingForCollection(sb, elementType, "__childSection", "__key", "__list", indent + " ");
  193. sb.AppendLine($"{indent} }}");
  194. sb.AppendLine($"{indent} target.{prop.Name} = __list;");
  195. sb.AppendLine($"{indent} }}");
  196. sb.AppendLine($"{indent}}}");
  197. sb.AppendLine();
  198. }
  199. /// <summary>
  200. /// 生成 HashSet 属性绑定
  201. /// </summary>
  202. private static void EmitHashSetPropertyBinding(StringBuilder sb, PropertyInfo prop, string indent)
  203. {
  204. var elementType = prop.ElementTypeName ?? "object";
  205. sb.AppendLine($"{indent}// {prop.Name}: {prop.TypeName}");
  206. sb.AppendLine($"{indent}{{");
  207. sb.AppendLine($"{indent} var __childSection = section.GetSection(\"{prop.Name}\");");
  208. sb.AppendLine($"{indent} var __keys = __childSection.GetChildKeys()");
  209. sb.AppendLine($"{indent} .Where(k => int.TryParse(k, out _))");
  210. sb.AppendLine($"{indent} .OrderBy(k => int.Parse(k))");
  211. sb.AppendLine($"{indent} .ToList();");
  212. sb.AppendLine($"{indent} if (__keys.Count > 0)");
  213. sb.AppendLine($"{indent} {{");
  214. sb.AppendLine($"{indent} var __set = new global::System.Collections.Generic.HashSet<{elementType}>();");
  215. sb.AppendLine($"{indent} foreach (var __key in __keys)");
  216. sb.AppendLine($"{indent} {{");
  217. EmitElementBindingForSet(sb, elementType, "__childSection", "__key", "__set", indent + " ");
  218. sb.AppendLine($"{indent} }}");
  219. sb.AppendLine($"{indent} target.{prop.Name} = __set;");
  220. sb.AppendLine($"{indent} }}");
  221. sb.AppendLine($"{indent}}}");
  222. sb.AppendLine();
  223. }
  224. /// <summary>
  225. /// 生成 Dictionary 属性绑定
  226. /// </summary>
  227. private static void EmitDictionaryPropertyBinding(StringBuilder sb, PropertyInfo prop, string indent)
  228. {
  229. var keyType = prop.KeyTypeName ?? "string";
  230. var valueType = prop.ElementTypeName ?? "object";
  231. sb.AppendLine($"{indent}// {prop.Name}: {prop.TypeName}");
  232. sb.AppendLine($"{indent}{{");
  233. sb.AppendLine($"{indent} var __childSection = section.GetSection(\"{prop.Name}\");");
  234. sb.AppendLine($"{indent} var __keys = __childSection.GetChildKeys().ToList();");
  235. sb.AppendLine($"{indent} if (__keys.Count > 0)");
  236. sb.AppendLine($"{indent} {{");
  237. sb.AppendLine($"{indent} var __dict = new global::System.Collections.Generic.Dictionary<{keyType}, {valueType}>();");
  238. sb.AppendLine($"{indent} foreach (var __key in __keys)");
  239. sb.AppendLine($"{indent} {{");
  240. // 键转换
  241. if (keyType == "string")
  242. {
  243. sb.AppendLine($"{indent} var __dictKey = __key;");
  244. }
  245. else
  246. {
  247. var keyConvert = GetConvertCode(keyType, "__key");
  248. sb.AppendLine($"{indent} var __dictKeyNullable = {keyConvert};");
  249. sb.AppendLine($"{indent} if (__dictKeyNullable == null) continue;");
  250. sb.AppendLine($"{indent} var __dictKey = __dictKeyNullable.Value;");
  251. }
  252. EmitElementBindingForDictionary(sb, valueType, "__childSection", "__key", "__dict", "__dictKey", indent + " ");
  253. sb.AppendLine($"{indent} }}");
  254. sb.AppendLine($"{indent} target.{prop.Name} = __dict;");
  255. sb.AppendLine($"{indent} }}");
  256. sb.AppendLine($"{indent}}}");
  257. sb.AppendLine();
  258. }
  259. /// <summary>
  260. /// 生成复杂对象属性绑定
  261. /// </summary>
  262. private static void EmitComplexPropertyBinding(StringBuilder sb, PropertyInfo prop, string indent)
  263. {
  264. // 获取全局限定的类型名,移除可空标记
  265. var baseTypeName = prop.TypeName.TrimEnd('?');
  266. var globalTypeName = baseTypeName.StartsWith("global::") ? baseTypeName : $"global::{baseTypeName}";
  267. sb.AppendLine($"{indent}// {prop.Name}: {prop.TypeName} (复杂对象)");
  268. sb.AppendLine($"{indent}{{");
  269. sb.AppendLine($"{indent} var __childSection = section.GetSection(\"{prop.Name}\");");
  270. sb.AppendLine($"{indent} var __childKeys = __childSection.GetChildKeys().ToList();");
  271. sb.AppendLine($"{indent} if (__childKeys.Count > 0)");
  272. sb.AppendLine($"{indent} {{");
  273. // 使用生成的 BindFrom 方法
  274. sb.AppendLine($"{indent} target.{prop.Name} = {globalTypeName}.BindFrom(__childSection);");
  275. sb.AppendLine($"{indent} }}");
  276. sb.AppendLine($"{indent}}}");
  277. sb.AppendLine();
  278. }
  279. /// <summary>
  280. /// 生成元素绑定代码(用于数组)
  281. /// </summary>
  282. private static void EmitElementBinding(StringBuilder sb, string elementType, string sectionVar, string keyVar, string targetVar, string indent)
  283. {
  284. if (IsSimpleTypeName(elementType))
  285. {
  286. var baseType = elementType.TrimEnd('?');
  287. sb.AppendLine($"{indent}var __elemValue = {sectionVar}.Get({keyVar});");
  288. sb.AppendLine($"{indent}if (__elemValue != null)");
  289. sb.AppendLine($"{indent}{{");
  290. var convertCode = GetConvertCode(elementType, "__elemValue");
  291. // string 是引用类型,直接赋值
  292. if (baseType == "string" || baseType == "System.String")
  293. {
  294. sb.AppendLine($"{indent} {targetVar} = {convertCode};");
  295. }
  296. else
  297. {
  298. sb.AppendLine($"{indent} var __converted = {convertCode};");
  299. sb.AppendLine($"{indent} if (__converted != null) {targetVar} = __converted.Value;");
  300. }
  301. sb.AppendLine($"{indent}}}");
  302. }
  303. else
  304. {
  305. var globalType = elementType.StartsWith("global::") ? elementType : $"global::{elementType}";
  306. sb.AppendLine($"{indent}var __elemSection = {sectionVar}.GetSection({keyVar});");
  307. sb.AppendLine($"{indent}{targetVar} = {globalType}.BindFrom(__elemSection);");
  308. }
  309. }
  310. /// <summary>
  311. /// 生成元素绑定代码(用于 List)
  312. /// </summary>
  313. private static void EmitElementBindingForCollection(StringBuilder sb, string elementType, string sectionVar, string keyVar, string listVar, string indent)
  314. {
  315. if (IsSimpleTypeName(elementType))
  316. {
  317. var baseType = elementType.TrimEnd('?');
  318. sb.AppendLine($"{indent}var __elemValue = {sectionVar}.Get({keyVar});");
  319. sb.AppendLine($"{indent}if (__elemValue != null)");
  320. sb.AppendLine($"{indent}{{");
  321. var convertCode = GetConvertCode(elementType, "__elemValue");
  322. // string 是引用类型,直接添加
  323. if (baseType == "string" || baseType == "System.String")
  324. {
  325. sb.AppendLine($"{indent} {listVar}.Add({convertCode});");
  326. }
  327. else
  328. {
  329. sb.AppendLine($"{indent} var __converted = {convertCode};");
  330. sb.AppendLine($"{indent} if (__converted != null) {listVar}.Add(__converted.Value);");
  331. }
  332. sb.AppendLine($"{indent}}}");
  333. }
  334. else
  335. {
  336. var globalType = elementType.StartsWith("global::") ? elementType : $"global::{elementType}";
  337. sb.AppendLine($"{indent}var __elemSection = {sectionVar}.GetSection({keyVar});");
  338. sb.AppendLine($"{indent}var __elem = {globalType}.BindFrom(__elemSection);");
  339. sb.AppendLine($"{indent}if (__elem != null) {listVar}.Add(__elem);");
  340. }
  341. }
  342. /// <summary>
  343. /// 生成元素绑定代码(用于 HashSet)
  344. /// </summary>
  345. private static void EmitElementBindingForSet(StringBuilder sb, string elementType, string sectionVar, string keyVar, string setVar, string indent)
  346. {
  347. if (IsSimpleTypeName(elementType))
  348. {
  349. var baseType = elementType.TrimEnd('?');
  350. sb.AppendLine($"{indent}var __elemValue = {sectionVar}.Get({keyVar});");
  351. sb.AppendLine($"{indent}if (__elemValue != null)");
  352. sb.AppendLine($"{indent}{{");
  353. var convertCode = GetConvertCode(elementType, "__elemValue");
  354. // string 是引用类型,直接添加
  355. if (baseType == "string" || baseType == "System.String")
  356. {
  357. sb.AppendLine($"{indent} {setVar}.Add({convertCode});");
  358. }
  359. else
  360. {
  361. sb.AppendLine($"{indent} var __converted = {convertCode};");
  362. sb.AppendLine($"{indent} if (__converted != null) {setVar}.Add(__converted.Value);");
  363. }
  364. sb.AppendLine($"{indent}}}");
  365. }
  366. else
  367. {
  368. var globalType = elementType.StartsWith("global::") ? elementType : $"global::{elementType}";
  369. sb.AppendLine($"{indent}var __elemSection = {sectionVar}.GetSection({keyVar});");
  370. sb.AppendLine($"{indent}var __elem = {globalType}.BindFrom(__elemSection);");
  371. sb.AppendLine($"{indent}if (__elem != null) {setVar}.Add(__elem);");
  372. }
  373. }
  374. /// <summary>
  375. /// 生成元素绑定代码(用于 Dictionary)
  376. /// </summary>
  377. private static void EmitElementBindingForDictionary(StringBuilder sb, string valueType, string sectionVar, string keyVar, string dictVar, string dictKeyVar, string indent)
  378. {
  379. if (IsSimpleTypeName(valueType))
  380. {
  381. var baseType = valueType.TrimEnd('?');
  382. sb.AppendLine($"{indent}var __elemValue = {sectionVar}.Get({keyVar});");
  383. sb.AppendLine($"{indent}if (__elemValue != null)");
  384. sb.AppendLine($"{indent}{{");
  385. var convertCode = GetConvertCode(valueType, "__elemValue");
  386. // string 是引用类型,直接赋值
  387. if (baseType == "string" || baseType == "System.String")
  388. {
  389. sb.AppendLine($"{indent} {dictVar}[{dictKeyVar}] = {convertCode};");
  390. }
  391. else
  392. {
  393. sb.AppendLine($"{indent} var __converted = {convertCode};");
  394. sb.AppendLine($"{indent} if (__converted != null) {dictVar}[{dictKeyVar}] = __converted.Value;");
  395. }
  396. sb.AppendLine($"{indent}}}");
  397. }
  398. else
  399. {
  400. var globalType = valueType.StartsWith("global::") ? valueType : $"global::{valueType}";
  401. sb.AppendLine($"{indent}var __elemSection = {sectionVar}.GetSection({keyVar});");
  402. sb.AppendLine($"{indent}var __elem = {globalType}.BindFrom(__elemSection);");
  403. sb.AppendLine($"{indent}if (__elem != null) {dictVar}[{dictKeyVar}] = __elem;");
  404. }
  405. }
  406. /// <summary>
  407. /// 获取类型转换代码
  408. /// </summary>
  409. private static string GetConvertCode(string typeName, string valueVar)
  410. {
  411. // 移除可空标记
  412. var baseType = typeName.TrimEnd('?');
  413. return baseType switch
  414. {
  415. "string" => valueVar,
  416. "int" or "System.Int32" => $"int.TryParse({valueVar}, out var __intVal) ? __intVal : (int?)null",
  417. "long" or "System.Int64" => $"long.TryParse({valueVar}, out var __longVal) ? __longVal : (long?)null",
  418. "short" or "System.Int16" => $"short.TryParse({valueVar}, out var __shortVal) ? __shortVal : (short?)null",
  419. "byte" or "System.Byte" => $"byte.TryParse({valueVar}, out var __byteVal) ? __byteVal : (byte?)null",
  420. "sbyte" or "System.SByte" => $"sbyte.TryParse({valueVar}, out var __sbyteVal) ? __sbyteVal : (sbyte?)null",
  421. "uint" or "System.UInt32" => $"uint.TryParse({valueVar}, out var __uintVal) ? __uintVal : (uint?)null",
  422. "ulong" or "System.UInt64" => $"ulong.TryParse({valueVar}, out var __ulongVal) ? __ulongVal : (ulong?)null",
  423. "ushort" or "System.UInt16" => $"ushort.TryParse({valueVar}, out var __ushortVal) ? __ushortVal : (ushort?)null",
  424. "float" or "System.Single" => $"float.TryParse({valueVar}, global::System.Globalization.NumberStyles.Float, global::System.Globalization.CultureInfo.InvariantCulture, out var __floatVal) ? __floatVal : (float?)null",
  425. "double" or "System.Double" => $"double.TryParse({valueVar}, global::System.Globalization.NumberStyles.Float, global::System.Globalization.CultureInfo.InvariantCulture, out var __doubleVal) ? __doubleVal : (double?)null",
  426. "decimal" or "System.Decimal" => $"decimal.TryParse({valueVar}, global::System.Globalization.NumberStyles.Number, global::System.Globalization.CultureInfo.InvariantCulture, out var __decimalVal) ? __decimalVal : (decimal?)null",
  427. "bool" or "System.Boolean" => $"bool.TryParse({valueVar}, out var __boolVal) ? __boolVal : (bool?)null",
  428. "char" or "System.Char" => $"({valueVar}.Length > 0 ? {valueVar}[0] : (char?)null)",
  429. "System.DateTime" => $"global::System.DateTime.TryParse({valueVar}, out var __dtVal) ? __dtVal : (global::System.DateTime?)null",
  430. "System.DateTimeOffset" => $"global::System.DateTimeOffset.TryParse({valueVar}, out var __dtoVal) ? __dtoVal : (global::System.DateTimeOffset?)null",
  431. "System.TimeSpan" => $"global::System.TimeSpan.TryParse({valueVar}, out var __tsVal) ? __tsVal : (global::System.TimeSpan?)null",
  432. "System.Guid" => $"global::System.Guid.TryParse({valueVar}, out var __guidVal) ? __guidVal : (global::System.Guid?)null",
  433. "System.DateOnly" => $"global::System.DateOnly.TryParse({valueVar}, out var __doVal) ? __doVal : (global::System.DateOnly?)null",
  434. "System.TimeOnly" => $"global::System.TimeOnly.TryParse({valueVar}, out var __toVal) ? __toVal : (global::System.TimeOnly?)null",
  435. "System.Uri" => $"global::System.Uri.TryCreate({valueVar}, global::System.UriKind.RelativeOrAbsolute, out var __uriVal) ? __uriVal : null",
  436. _ when baseType.Contains(".") => $"global::System.Enum.TryParse<{baseType}>({valueVar}, true, out var __enumVal) ? __enumVal : ({baseType}?)null",
  437. _ => $"global::System.Enum.TryParse<{baseType}>({valueVar}, true, out var __enumVal) ? __enumVal : ({baseType}?)null"
  438. };
  439. }
  440. /// <summary>
  441. /// 判断是否为简单类型名
  442. /// </summary>
  443. private static bool IsSimpleTypeName(string typeName)
  444. {
  445. var baseType = typeName.TrimEnd('?');
  446. return baseType switch
  447. {
  448. "string" or "int" or "long" or "short" or "byte" or "sbyte" or
  449. "uint" or "ulong" or "ushort" or "float" or "double" or "decimal" or
  450. "bool" or "char" or
  451. "System.String" or "System.Int32" or "System.Int64" or "System.Int16" or
  452. "System.Byte" or "System.SByte" or "System.UInt32" or "System.UInt64" or
  453. "System.UInt16" or "System.Single" or "System.Double" or "System.Decimal" or
  454. "System.Boolean" or "System.Char" or
  455. "System.DateTime" or "System.DateTimeOffset" or "System.TimeSpan" or
  456. "System.Guid" or "System.Uri" or "System.DateOnly" or "System.TimeOnly" => true,
  457. _ => false
  458. };
  459. }
  460. /// <summary>
  461. /// 生成扩展方法类
  462. /// </summary>
  463. public static string EmitExtensionsClass(ImmutableArray<ConfigClassInfo> classes)
  464. {
  465. var sb = new StringBuilder();
  466. sb.AppendLine("// <auto-generated/>");
  467. sb.AppendLine("// 此文件由 Apq.Cfg.SourceGenerator 自动生成,请勿手动修改");
  468. sb.AppendLine("#nullable enable");
  469. sb.AppendLine();
  470. sb.AppendLine("namespace Apq.Cfg");
  471. sb.AppendLine("{");
  472. sb.AppendLine(" /// <summary>");
  473. sb.AppendLine(" /// ICfgRoot 扩展方法(由源生成器生成)");
  474. sb.AppendLine(" /// </summary>");
  475. sb.AppendLine(" [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]");
  476. sb.AppendLine(" internal static class CfgRootGeneratedExtensions");
  477. sb.AppendLine(" {");
  478. foreach (var classInfo in classes)
  479. {
  480. if (!classInfo.GenerateExtension)
  481. continue;
  482. var methodName = $"Get{classInfo.ClassName}";
  483. var sectionPath = classInfo.SectionPath;
  484. sb.AppendLine($" /// <summary>");
  485. sb.AppendLine($" /// 获取 {classInfo.ClassName} 配置(零反射)");
  486. sb.AppendLine($" /// </summary>");
  487. sb.AppendLine($" public static {classInfo.FullTypeName} {methodName}(this global::Apq.Cfg.ICfgRoot cfg)");
  488. sb.AppendLine($" {{");
  489. sb.AppendLine($" return {classInfo.FullTypeName}.BindFrom(cfg.GetSection(\"{sectionPath}\"));");
  490. sb.AppendLine($" }}");
  491. sb.AppendLine();
  492. }
  493. sb.AppendLine(" }");
  494. sb.AppendLine("}");
  495. return sb.ToString();
  496. }
  497. }