From 14894814a515d49f50fbc393f6785ca43dc5aab2 Mon Sep 17 00:00:00 2001 From: GeWuYou <95328647+GeWuYou@users.noreply.github.com> Date: Wed, 14 Jan 2026 13:36:32 +0800 Subject: [PATCH] =?UTF-8?q?refactor(logging):=20=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E6=97=A5=E5=BF=97=E7=94=9F=E6=88=90=E5=99=A8=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加AttributeData扩展方法用于获取命名参数和构造函数参数 - 引入GenericInfo记录结构体处理泛型信息 - 将INamedTypeSymbol扩展方法转换为扩展方法语法 - 添加ResolveGenerics方法解析泛型参数和约束条件 - 简化LoggerGenerator中的参数解析逻辑 - 移除不再需要的GetNamedArg私有方法 - 优化代码可读性和维护性 --- .../extensions/AttributeDataExtensions.cs | 47 +++++++ .../extensions/INamedTypeSymbolExtensions.cs | 115 ++++++++++++------ .../info/GenericInfo.cs | 13 ++ .../logging/LoggerGenerator.cs | 101 ++++----------- 4 files changed, 167 insertions(+), 109 deletions(-) create mode 100644 GFramework.SourceGenerators.Common/extensions/AttributeDataExtensions.cs create mode 100644 GFramework.SourceGenerators.Common/info/GenericInfo.cs diff --git a/GFramework.SourceGenerators.Common/extensions/AttributeDataExtensions.cs b/GFramework.SourceGenerators.Common/extensions/AttributeDataExtensions.cs new file mode 100644 index 0000000..6aaeece --- /dev/null +++ b/GFramework.SourceGenerators.Common/extensions/AttributeDataExtensions.cs @@ -0,0 +1,47 @@ +using System; +using Microsoft.CodeAnalysis; + +namespace GFramework.SourceGenerators.Common.extensions; + +/// +/// 提供AttributeData的扩展方法 +/// +public static class AttributeDataExtensions +{ + /// 特性数据对象 + extension(AttributeData attr) + { + /// + /// 从特性数据中获取指定名称的命名参数值 + /// + /// 期望返回的参数类型 + /// 要查找的命名参数名称 + /// 当找不到指定参数时返回的默认值 + /// 找到的参数值,如果未找到或类型不匹配则返回默认值 + public T? GetNamedArgument(string name, + T? defaultValue = default) + { + // 遍历所有命名参数以查找匹配的键值对 + foreach (var kv in attr.NamedArguments) + { + if (string.Equals(kv.Key, name, StringComparison.Ordinal) && kv.Value.Value is T t) + return t; + } + + return defaultValue; + } + + /// + /// 获取特性构造函数的第一个参数作为字符串值 + /// + /// 当没有构造函数参数或第一个参数不是字符串时返回的默认值 + /// 构造函数第一个参数的字符串值,如果不存在则返回默认值 + public string GetFirstCtorString(string defaultValue) + { + if (attr.ConstructorArguments.Length == 0) + return defaultValue; + + return attr.ConstructorArguments[0].Value as string ?? defaultValue; + } + } +} \ No newline at end of file diff --git a/GFramework.SourceGenerators.Common/extensions/INamedTypeSymbolExtensions.cs b/GFramework.SourceGenerators.Common/extensions/INamedTypeSymbolExtensions.cs index 4865ea9..291efb5 100644 --- a/GFramework.SourceGenerators.Common/extensions/INamedTypeSymbolExtensions.cs +++ b/GFramework.SourceGenerators.Common/extensions/INamedTypeSymbolExtensions.cs @@ -1,4 +1,6 @@ using System.Collections.Generic; +using System.Linq; +using GFramework.SourceGenerators.Common.info; using Microsoft.CodeAnalysis; namespace GFramework.SourceGenerators.Common.extensions; @@ -8,45 +10,12 @@ namespace GFramework.SourceGenerators.Common.extensions; /// public static class INamedTypeSymbolExtensions { - /// - /// 获取命名类型符号的完整类名(包括嵌套类型名称) - /// - /// 要获取完整类名的命名类型符号 - /// 完整的类名,格式为"外层类名.内层类名.当前类名" - public static string GetFullClassName(this INamedTypeSymbol symbol) - { - var names = new Stack(); - var current = symbol; - - // 遍历包含类型链,将所有类型名称压入栈中 - while (current != null) - { - names.Push(current.Name); - current = current.ContainingType; - } - - // 将栈中的名称用点号连接,形成完整的类名 - return string.Join(".", names); - } - - /// - /// 获取命名类型符号的命名空间名称 - /// - /// 要获取命名空间的命名类型符号 - /// 命名空间名称,如果是全局命名空间则返回null - private static string? GetNamespace(this INamedTypeSymbol symbol) - { - return symbol.ContainingNamespace.IsGlobalNamespace - ? null - : symbol.ContainingNamespace.ToDisplayString(); - } - /// /// 根据类型种类解析对应的类型关键字 /// /// 要解析类型的命名类型符号 /// 对应类型的字符串表示,如"class"、"struct"或"record" - private static string ResolveTypeKind(this INamedTypeSymbol symbol) + public static string ResolveTypeKind(this INamedTypeSymbol symbol) { return symbol.TypeKind switch { @@ -58,4 +27,82 @@ public static class INamedTypeSymbolExtensions _ => "class", }; } + + /// + /// 解析泛型信息,包括泛型参数和约束条件 + /// + /// 要解析泛型信息的命名类型符号 + /// 包含泛型参数和约束条件的GenericInfo对象 + public static GenericInfo ResolveGenerics(this INamedTypeSymbol symbol) + { + if (symbol.TypeParameters.Length == 0) + return new GenericInfo(string.Empty, []); + + // 构建泛型参数列表 + var parameters = + "<" + string.Join(", ", symbol.TypeParameters.Select(tp => tp.Name)) + ">"; + + // 构建泛型约束条件列表 + var constraints = symbol.TypeParameters + .Select(BuildConstraint) + .Where(c => c != null) + .Cast() + .ToList(); + + return new GenericInfo(parameters, constraints); + } + + /// + /// 构建单个类型参数的约束条件字符串 + /// + /// 类型参数符号 + /// 约束条件的字符串表示,如果没有约束则返回null + private static string? BuildConstraint(ITypeParameterSymbol tp) + { + var parts = new List(); + + if (tp.HasReferenceTypeConstraint) parts.Add("class"); + if (tp.HasValueTypeConstraint) parts.Add("struct"); + parts.AddRange(tp.ConstraintTypes.Select(t => t.ToDisplayString())); + if (tp.HasConstructorConstraint) parts.Add("new()"); + + return parts.Count == 0 + ? null + : $"where {tp.Name} : {string.Join(", ", parts)}"; + } + + /// 要获取完整类名的命名类型符号 + extension(INamedTypeSymbol symbol) + { + /// + /// 获取命名类型符号的完整类名(包括嵌套类型名称) + /// + /// 完整的类名,格式为"外层类名.内层类名.当前类名" + public string GetFullClassName() + { + var names = new Stack(); + var current = symbol; + + // 遍历包含类型链,将所有类型名称压入栈中 + while (current != null) + { + names.Push(current.Name); + current = current.ContainingType; + } + + // 将栈中的名称用点号连接,形成完整的类名 + return string.Join(".", names); + } + + /// + /// 获取命名类型符号的命名空间名称 + /// + /// 命名空间名称,如果是全局命名空间则返回null + public string? GetNamespace() + { + return symbol.ContainingNamespace.IsGlobalNamespace + ? null + : symbol.ContainingNamespace.ToDisplayString(); + } + } } \ No newline at end of file diff --git a/GFramework.SourceGenerators.Common/info/GenericInfo.cs b/GFramework.SourceGenerators.Common/info/GenericInfo.cs new file mode 100644 index 0000000..05869cf --- /dev/null +++ b/GFramework.SourceGenerators.Common/info/GenericInfo.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace GFramework.SourceGenerators.Common.info; + +/// +/// 表示泛型信息的数据结构 +/// +/// 泛型参数字符串 +/// 泛型约束列表 +public record struct GenericInfo( + string Parameters, + IReadOnlyList Constraints +); \ No newline at end of file diff --git a/GFramework.SourceGenerators/logging/LoggerGenerator.cs b/GFramework.SourceGenerators/logging/LoggerGenerator.cs index 577ccff..0ccb722 100644 --- a/GFramework.SourceGenerators/logging/LoggerGenerator.cs +++ b/GFramework.SourceGenerators/logging/LoggerGenerator.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; using System.Text; using GFramework.SourceGenerators.Abstractions.logging; using GFramework.SourceGenerators.Common.constants; @@ -56,84 +54,42 @@ public sealed class LoggerGenerator : TypeAttributeClassGeneratorBase INamedTypeSymbol symbol, AttributeData attr) { - var ns = symbol.ContainingNamespace.IsGlobalNamespace - ? null - : symbol.ContainingNamespace.ToDisplayString(); - - // 类名和嵌套类完整路径 + var ns = symbol.GetNamespace(); var className = symbol.GetFullClassName(); + var typeKind = symbol.ResolveTypeKind(); + var generics = symbol.ResolveGenerics(); - // 构造函数参数解析 - var logName = className; - if (attr.ConstructorArguments.Length > 0) - { - var argValue = attr.ConstructorArguments[0].Value; - logName = argValue is string s && !string.IsNullOrWhiteSpace(s) - ? s - : className; - } - - // 命名参数解析 - var fieldName = GetNamedArg(attr, "FieldName")?.ToString() ?? "_log"; - var access = GetNamedArg(attr, "AccessModifier")?.ToString() ?? "private"; - var isStaticObj = GetNamedArg(attr, "IsStatic"); - var isStatic = isStaticObj is not bool b || b; // 默认 true + var logName = attr.GetFirstCtorString(className); + var fieldName = attr.GetNamedArgument("FieldName", "_log"); + var access = attr.GetNamedArgument("AccessModifier", "private"); + var isStatic = attr.GetNamedArgument("IsStatic", true); var staticKeyword = isStatic ? "static " : ""; - // 泛型参数 - var typeParams = symbol.TypeParameters.Length > 0 - ? "<" + string.Join(", ", symbol.TypeParameters.Select(tp => tp.Name)) + ">" - : ""; - - // 泛型约束 - var constraints = symbol.TypeParameters - .Where(tp => tp.ConstraintTypes.Length > 0 || tp.HasReferenceTypeConstraint || tp.HasValueTypeConstraint || - tp.HasConstructorConstraint) - .Select(tp => - { - var parts = new List(); - - if (tp.HasReferenceTypeConstraint) parts.Add("class"); - if (tp.HasValueTypeConstraint) parts.Add("struct"); - parts.AddRange(tp.ConstraintTypes.Select(t => t.ToDisplayString())); - if (tp.HasConstructorConstraint) parts.Add("new()"); - - return $"where {tp.Name} : {string.Join(", ", parts)}"; - }); - - // 判断类型 - var typeKind = symbol.TypeKind switch - { - TypeKind.Class => "class", - TypeKind.Struct => "struct", -#if NET5_0_OR_GREATER || ROSLYN_3_7_OR_GREATER - TypeKind.Record => "record", -#endif - _ => "class" - }; - - var sb = new StringBuilder(); - sb.AppendLine("// "); - sb.AppendLine($"using {PathContests.CoreAbstractionsNamespace}.logging;"); - sb.AppendLine($"using {PathContests.CoreNamespace}.logging;"); + var sb = new StringBuilder() + .AppendLine("// ") + .AppendLine($"using {PathContests.CoreAbstractionsNamespace}.logging;") + .AppendLine($"using {PathContests.CoreNamespace}.logging;"); if (ns is not null) { - sb.AppendLine($"namespace {ns};"); - sb.AppendLine(); + sb.AppendLine() + .AppendLine($"namespace {ns};"); } - // 生成 partial 类 - sb.AppendLine($"partial {typeKind} {className}{typeParams}"); - foreach (var c in constraints) - sb.AppendLine($" {c}"); - sb.AppendLine("{"); - sb.AppendLine($" /// Auto-generated logger"); - sb.AppendLine( - $" {access} {staticKeyword}readonly ILogger {fieldName} = LoggerFactoryResolver.Provider.CreateLogger(\"{logName}\");"); - sb.AppendLine("}"); + sb.AppendLine() + .AppendLine($"partial {typeKind} {className}{generics.Parameters}"); - return sb.ToString().TrimEnd(); + foreach (var c in generics.Constraints) + sb.AppendLine($" {c}"); + + sb.AppendLine("{") + .AppendLine(" /// Auto-generated logger") + .AppendLine( + $" {access} {staticKeyword}readonly ILogger {fieldName} = " + + $"LoggerFactoryResolver.Provider.CreateLogger(\"{logName}\");") + .AppendLine("}"); + + return sb.ToString(); } @@ -144,9 +100,4 @@ public sealed class LoggerGenerator : TypeAttributeClassGeneratorBase { return $"{symbol.Name}.Logger.g.cs"; } - - private static object? GetNamedArg(AttributeData attr, string name) - { - return attr.NamedArguments.FirstOrDefault(kv => kv.Key == name).Value.Value; - } } \ No newline at end of file