Skip to content

Commit 4dc12a9

Browse files
committed
refactor(options): 提取布尔值解析逻辑并优化反射性能
- 提取 IsBooleanValue 和 ParseBooleanValue 到共享工具类 BooleanParser - 添加 ConcurrentDictionary 缓存反射结果提升性能 - 添加 args 参数空值检查 - 使用 StringBuilder 优化字符串拼接 - 将 CommandLineArgumentConverter 标记为 sealed - 修复 key=value 格式解析时空值的处理
1 parent 829542e commit 4dc12a9

3 files changed

Lines changed: 123 additions & 71 deletions

File tree

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// ==========================================================================================
2+
// GameFrameX 组织及其衍生项目的版权、商标、专利及其他相关权利
3+
// GameFrameX organization and its derivative projects' copyrights, trademarks, patents, and related rights
4+
// 均受中华人民共和国及相关国际法律法规保护。
5+
// are protected by the laws of the People's Republic of China and relevant international regulations.
6+
//
7+
// 使用本项目须严格遵守相应法律法规及开源许可证之规定。
8+
// Usage of this project must strictly comply with applicable laws, regulations, and open-source licenses.
9+
//
10+
// 本项目采用 MIT 许可证与 Apache License 2.0 双许可证分发,
11+
// This project is dual-licensed under the MIT License and Apache License 2.0,
12+
// 完整许可证文本请参见源代码根目录下的 LICENSE 文件。
13+
// please refer to the LICENSE file in the root directory of the source code for the full license text.
14+
//
15+
// 禁止利用本项目实施任何危害国家安全、破坏社会秩序、
16+
// It is prohibited to use this project to engage in any activities that endanger national security, disrupt social order,
17+
// 侵犯他人合法权益等法律法规所禁止的行为!
18+
// or infringe upon the legitimate rights and interests of others, as prohibited by laws and regulations!
19+
// 因基于本项目二次开发所产生的一切法律纠纷与责任,
20+
// Any legal disputes and liabilities arising from secondary development based on this project
21+
// 本项目组织与贡献者概不承担。
22+
// shall be borne solely by the developer; the project organization and contributors assume no responsibility.
23+
//
24+
// GitHub 仓库:https://github.com/GameFrameX
25+
// GitHub Repository: https://github.com/GameFrameX
26+
// Gitee 仓库:https://gitee.com/GameFrameX
27+
// Gitee Repository: https://gitee.com/GameFrameX
28+
// CNB 仓库:https://cnb.cool/GameFrameX
29+
// CNB Repository: https://cnb.cool/GameFrameX
30+
// 官方文档:https://gameframex.doc.alianblank.com/
31+
// Official Documentation: https://gameframex.doc.alianblank.com/
32+
// ==========================================================================================
33+
34+
namespace GameFrameX.Foundation.Options;
35+
36+
/// <summary>
37+
/// 布尔值解析工具类
38+
/// Boolean value parsing utility class
39+
/// </summary>
40+
internal static class BooleanParser
41+
{
42+
/// <summary>
43+
/// 检查字符串值是否为布尔类型
44+
/// Checks if the string value is a boolean type
45+
/// </summary>
46+
/// <param name="value">要检查的字符串值 / The string value to check</param>
47+
/// <returns>如果是布尔类型值则返回true,否则返回false / Returns true if the value is a boolean type, otherwise false</returns>
48+
public static bool IsBooleanValue(string value)
49+
{
50+
if (string.IsNullOrWhiteSpace(value))
51+
{
52+
return false;
53+
}
54+
55+
var normalizedValue = value.Trim().ToLowerInvariant();
56+
return normalizedValue is "true" or "false" or "1" or "0" or "yes" or "no" or "on" or "off";
57+
}
58+
59+
/// <summary>
60+
/// 解析布尔类型字符串值
61+
/// Parses a boolean string value
62+
/// </summary>
63+
/// <param name="value">要解析的字符串值 / The string value to parse</param>
64+
/// <returns>解析后的布尔值 / The parsed boolean value</returns>
65+
public static bool ParseBooleanValue(string value)
66+
{
67+
if (string.IsNullOrWhiteSpace(value))
68+
{
69+
return false;
70+
}
71+
72+
var normalizedValue = value.Trim().ToLowerInvariant();
73+
return normalizedValue is "true" or "1" or "yes" or "on";
74+
}
75+
}

GameFrameX.Foundation.Options/CommandLineArgumentConverter.cs

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ namespace GameFrameX.Foundation.Options;
3636
/// <summary>
3737
/// 命令行参数转换器
3838
/// </summary>
39-
public class CommandLineArgumentConverter
39+
public sealed class CommandLineArgumentConverter
4040
{
4141
/// <summary>
4242
/// 布尔参数格式
@@ -144,7 +144,8 @@ public List<string> ConvertToStandardFormat(string[] args)
144144
{
145145
var parts = arg.Split(new[] { '=' }, 2);
146146
var key = parts[0];
147-
var value = parts[1];
147+
// 如果值部分为空(如 --key=),则使用空字符串
148+
var value = parts.Length > 1 ? parts[1] : string.Empty;
148149

149150
// 根据EnsurePrefixedKeys设置处理键
150151
if (!key.StartsWith("-") && EnsurePrefixedKeys)
@@ -204,7 +205,7 @@ public List<string> ConvertToStandardFormat(string[] args)
204205
if (!nextArg.StartsWith("-"))
205206
{
206207
// 对于布尔标志格式,检查是否为布尔值
207-
if (BoolFormat == BoolArgumentFormat.Flag && IsBooleanValue(nextArg))
208+
if (BoolFormat == BoolArgumentFormat.Flag && BooleanParser.IsBooleanValue(nextArg))
208209
{
209210
// 跳过布尔值,因为标志格式不需要显式值
210211
i++;
@@ -226,20 +227,4 @@ public List<string> ConvertToStandardFormat(string[] args)
226227
throw new ArgumentException($"处理命令行参数时发生错误 (An error occurred while processing command-line arguments): {ex.Message}", ex);
227228
}
228229
}
229-
230-
/// <summary>
231-
/// 检查字符串值是否为Bool类型
232-
/// </summary>
233-
/// <param name="value">要检查的字符串值</param>
234-
/// <returns>如果是Bool类型值则返回true,否则返回false</returns>
235-
private static bool IsBooleanValue(string value)
236-
{
237-
if (string.IsNullOrWhiteSpace(value))
238-
{
239-
return false;
240-
}
241-
242-
var normalizedValue = value.Trim().ToLowerInvariant();
243-
return normalizedValue is "true" or "false" or "1" or "0" or "yes" or "no" or "on" or "off";
244-
}
245230
}

GameFrameX.Foundation.Options/OptionsBuilder.cs

Lines changed: 44 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
// Official Documentation: https://gameframex.doc.alianblank.com/
3232
// ==========================================================================================
3333

34+
using System.Collections.Concurrent;
3435
using System.Reflection;
3536
using GameFrameX.Foundation.Options.Attributes;
3637

@@ -52,7 +53,7 @@ public class OptionsBuilder
5253
/// <returns>构建的配置选项对象</returns>
5354
public static TOptions Create<TOptions>(string[] args, bool skipValidation = false) where TOptions : class, new()
5455
{
55-
var builder = new OptionsBuilder<TOptions>(args);
56+
var builder = new OptionsBuilder<TOptions>(args ?? Array.Empty<string>());
5657
return builder.Build(skipValidation);
5758
}
5859

@@ -73,7 +74,7 @@ public static TOptions Create<TOptions>(
7374
bool useEnvironmentVariables = true,
7475
bool skipValidation = false) where TOptions : class, new()
7576
{
76-
var builder = new OptionsBuilder<TOptions>(args, boolFormat, ensurePrefixedKeys, useEnvironmentVariables);
77+
var builder = new OptionsBuilder<TOptions>(args ?? Array.Empty<string>(), boolFormat, ensurePrefixedKeys, useEnvironmentVariables);
7778
return builder.Build(skipValidation);
7879
}
7980

@@ -86,7 +87,7 @@ public static TOptions Create<TOptions>(
8687
/// <returns>构建的配置选项对象</returns>
8788
public static TOptions CreateFromArgsOnly<TOptions>(string[] args, bool skipValidation = false) where TOptions : class, new()
8889
{
89-
var builder = new OptionsBuilder<TOptions>(args, useEnvironmentVariables: false);
90+
var builder = new OptionsBuilder<TOptions>(args ?? Array.Empty<string>(), useEnvironmentVariables: false);
9091
return builder.Build(skipValidation);
9192
}
9293

@@ -125,7 +126,7 @@ public static TOptions Create<TOptions>(
125126
{
126127
try
127128
{
128-
result = Create<TOptions>(args);
129+
result = Create<TOptions>(args ?? Array.Empty<string>());
129130
error = null;
130131
return true;
131132
}
@@ -147,7 +148,7 @@ public static TOptions Create<TOptions>(
147148
public static TOptions CreateWithDebug<TOptions>(string[] args, bool skipValidation = false) where TOptions : class, new()
148149
{
149150
// 创建配置选项
150-
var result = Create<TOptions>(args, skipValidation);
151+
var result = Create<TOptions>(args ?? Array.Empty<string>(), skipValidation);
151152

152153
// 打印解析结果
153154
OptionsDebugger.PrintParsedOptions(result);
@@ -167,6 +168,23 @@ public static TOptions Create<TOptions>(
167168
/// </remarks>
168169
public sealed class OptionsBuilder<T> where T : class, new()
169170
{
171+
/// <summary>
172+
/// 反射结果缓存,用于缓存类型的属性信息
173+
/// Reflection result cache for caching type property information
174+
/// </summary>
175+
private static readonly ConcurrentDictionary<Type, PropertyInfo[]> PropertyCache = new();
176+
177+
/// <summary>
178+
/// 获取指定类型的所有属性(带缓存)
179+
/// Gets all properties of the specified type (with caching)
180+
/// </summary>
181+
/// <param name="type">要获取属性的类型 / The type to get properties for</param>
182+
/// <returns>属性信息数组 / Array of property information</returns>
183+
private static PropertyInfo[] GetCachedProperties(Type type)
184+
{
185+
return PropertyCache.GetOrAdd(type, t => t.GetProperties());
186+
}
187+
170188
private readonly string[] _args;
171189
private readonly bool _useEnvironmentVariables;
172190
private readonly bool _ensurePrefixedKeys;
@@ -267,7 +285,7 @@ public T Build(bool skipValidation = false)
267285
/// <param name="target">目标对象</param>
268286
private void ApplyDefaultValues(T target)
269287
{
270-
var properties = typeof(T).GetProperties()
288+
var properties = GetCachedProperties(typeof(T))
271289
.Where(p => p.CanWrite)
272290
.ToList();
273291

@@ -310,7 +328,7 @@ private void ApplyDefaultValues(T target)
310328
/// <param name="target">目标对象</param>
311329
private void ValidateRequiredOptions(T target)
312330
{
313-
var properties = typeof(T).GetProperties();
331+
var properties = GetCachedProperties(typeof(T));
314332
var missingOptions = new List<string>();
315333

316334
foreach (var property in properties)
@@ -362,7 +380,7 @@ private Dictionary<string, object> GetEnvironmentVariables()
362380
{
363381
// 获取所有环境变量
364382
var envVars = Environment.GetEnvironmentVariables();
365-
var properties = typeof(T).GetProperties();
383+
var properties = GetCachedProperties(typeof(T));
366384
var envVarMappings = new Dictionary<string, PropertyInfo>(StringComparer.OrdinalIgnoreCase);
367385

368386
// 收集环境变量映射
@@ -371,7 +389,7 @@ private Dictionary<string, object> GetEnvironmentVariables()
371389
var envVarAttrs = property.GetCustomAttributes<EnvironmentVariableAttribute>().ToList();
372390
foreach (var envVarAttr in envVarAttrs)
373391
{
374-
if (envVarAttr != null && !string.IsNullOrEmpty(envVarAttr.Name))
392+
if (!string.IsNullOrEmpty(envVarAttr.Name))
375393
{
376394
envVarMappings[envVarAttr.Name] = property;
377395
}
@@ -409,9 +427,9 @@ private Dictionary<string, object> GetEnvironmentVariables()
409427
}
410428

411429
// 处理布尔值
412-
if (property.PropertyType == typeof(bool) && IsBooleanValue(value))
430+
if (property.PropertyType == typeof(bool) && BooleanParser.IsBooleanValue(value))
413431
{
414-
result[property.Name] = ParseBooleanValue(value);
432+
result[property.Name] = BooleanParser.ParseBooleanValue(value);
415433
}
416434
else
417435
{
@@ -441,9 +459,9 @@ private Dictionary<string, object> GetEnvironmentVariables()
441459
if (matchedProperty != null)
442460
{
443461
// 处理布尔值
444-
if (matchedProperty.PropertyType == typeof(bool) && IsBooleanValue(value))
462+
if (matchedProperty.PropertyType == typeof(bool) && BooleanParser.IsBooleanValue(value))
445463
{
446-
result[matchedProperty.Name] = ParseBooleanValue(value);
464+
result[matchedProperty.Name] = BooleanParser.ParseBooleanValue(value);
447465
}
448466
else
449467
{
@@ -462,38 +480,6 @@ private Dictionary<string, object> GetEnvironmentVariables()
462480
return result;
463481
}
464482

465-
/// <summary>
466-
/// 检查字符串值是否为Bool类型
467-
/// </summary>
468-
/// <param name="value">要检查的字符串值</param>
469-
/// <returns>如果是Bool类型值则返回true,否则返回false</returns>
470-
private static bool IsBooleanValue(string value)
471-
{
472-
if (string.IsNullOrWhiteSpace(value))
473-
{
474-
return false;
475-
}
476-
477-
var normalizedValue = value.Trim().ToLowerInvariant();
478-
return normalizedValue is "true" or "false" or "1" or "0" or "yes" or "no" or "on" or "off";
479-
}
480-
481-
/// <summary>
482-
/// 解析Bool类型字符串值
483-
/// </summary>
484-
/// <param name="value">要解析的字符串值</param>
485-
/// <returns>解析后的Bool值</returns>
486-
private static bool ParseBooleanValue(string value)
487-
{
488-
if (string.IsNullOrWhiteSpace(value))
489-
{
490-
return false;
491-
}
492-
493-
var normalizedValue = value.Trim().ToLowerInvariant();
494-
return normalizedValue is "true" or "1" or "yes" or "on";
495-
}
496-
497483
/// <summary>
498484
/// 将标准格式参数转换为选项字典
499485
/// </summary>
@@ -564,7 +550,7 @@ private Dictionary<string, object> ConvertToOptionsDictionary(List<string> stand
564550
/// <returns>如果对应布尔属性则返回true,否则返回false</returns>
565551
private bool IsBooleanProperty(string key)
566552
{
567-
var properties = typeof(T).GetProperties();
553+
var properties = GetCachedProperties(typeof(T));
568554
var optionMappings = GetOptionMappings();
569555

570556
// 首先尝试通过选项映射查找属性
@@ -590,7 +576,7 @@ private bool IsBooleanProperty(string key)
590576
private Dictionary<string, string> GetOptionMappings()
591577
{
592578
var result = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
593-
var properties = typeof(T).GetProperties();
579+
var properties = GetCachedProperties(typeof(T));
594580

595581
foreach (var property in properties)
596582
{
@@ -649,7 +635,7 @@ private string NormalizeKey(string key)
649635
/// <param name="options">选项字典</param>
650636
private void ApplyOptions(T target, Dictionary<string, object> options)
651637
{
652-
var properties = typeof(T).GetProperties()
638+
var properties = GetCachedProperties(typeof(T))
653639
.Where(p => p.CanWrite)
654640
.ToList();
655641

@@ -686,7 +672,7 @@ private void ApplyOptions(T target, Dictionary<string, object> options)
686672
}
687673

688674
// 获取字符串值
689-
string stringValue = kvp.Value.ToString();
675+
string stringValue = kvp.Value?.ToString();
690676

691677
// 根据目标类型进行转换
692678
object convertedValue = null;
@@ -859,15 +845,21 @@ private string NormalizePropertyName(string key)
859845
return string.Empty;
860846
}
861847

862-
key = parts[0];
848+
var sb = new System.Text.StringBuilder(parts[0]);
863849

864850
for (int i = 1; i < parts.Length; i++)
865851
{
866-
if (!string.IsNullOrEmpty(parts[i]) && parts[i].Length > 0)
852+
if (!string.IsNullOrEmpty(parts[i]))
867853
{
868-
key += char.ToUpper(parts[i][0]) + (parts[i].Length > 1 ? parts[i].Substring(1) : "");
854+
sb.Append(char.ToUpperInvariant(parts[i][0]));
855+
if (parts[i].Length > 1)
856+
{
857+
sb.Append(parts[i].Substring(1));
858+
}
869859
}
870860
}
861+
862+
return sb.ToString();
871863
}
872864

873865
return key;

0 commit comments

Comments
 (0)