mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-22 10:34:30 +08:00
refactor(logging): 重构日志生成器代码结构
- 添加AttributeData扩展方法用于获取命名参数和构造函数参数 - 引入GenericInfo记录结构体处理泛型信息 - 将INamedTypeSymbol扩展方法转换为扩展方法语法 - 添加ResolveGenerics方法解析泛型参数和约束条件 - 简化LoggerGenerator中的参数解析逻辑 - 移除不再需要的GetNamedArg私有方法 - 优化代码可读性和维护性
This commit is contained in:
parent
7876647871
commit
14894814a5
@ -0,0 +1,47 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
|
||||||
|
namespace GFramework.SourceGenerators.Common.extensions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 提供AttributeData的扩展方法
|
||||||
|
/// </summary>
|
||||||
|
public static class AttributeDataExtensions
|
||||||
|
{
|
||||||
|
/// <param name="attr">特性数据对象</param>
|
||||||
|
extension(AttributeData attr)
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 从特性数据中获取指定名称的命名参数值
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">期望返回的参数类型</typeparam>
|
||||||
|
/// <param name="name">要查找的命名参数名称</param>
|
||||||
|
/// <param name="defaultValue">当找不到指定参数时返回的默认值</param>
|
||||||
|
/// <returns>找到的参数值,如果未找到或类型不匹配则返回默认值</returns>
|
||||||
|
public T? GetNamedArgument<T>(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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取特性构造函数的第一个参数作为字符串值
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="defaultValue">当没有构造函数参数或第一个参数不是字符串时返回的默认值</param>
|
||||||
|
/// <returns>构造函数第一个参数的字符串值,如果不存在则返回默认值</returns>
|
||||||
|
public string GetFirstCtorString(string defaultValue)
|
||||||
|
{
|
||||||
|
if (attr.ConstructorArguments.Length == 0)
|
||||||
|
return defaultValue;
|
||||||
|
|
||||||
|
return attr.ConstructorArguments[0].Value as string ?? defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using GFramework.SourceGenerators.Common.info;
|
||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
|
|
||||||
namespace GFramework.SourceGenerators.Common.extensions;
|
namespace GFramework.SourceGenerators.Common.extensions;
|
||||||
@ -8,45 +10,12 @@ namespace GFramework.SourceGenerators.Common.extensions;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class INamedTypeSymbolExtensions
|
public static class INamedTypeSymbolExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// 获取命名类型符号的完整类名(包括嵌套类型名称)
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="symbol">要获取完整类名的命名类型符号</param>
|
|
||||||
/// <returns>完整的类名,格式为"外层类名.内层类名.当前类名"</returns>
|
|
||||||
public static string GetFullClassName(this INamedTypeSymbol symbol)
|
|
||||||
{
|
|
||||||
var names = new Stack<string>();
|
|
||||||
var current = symbol;
|
|
||||||
|
|
||||||
// 遍历包含类型链,将所有类型名称压入栈中
|
|
||||||
while (current != null)
|
|
||||||
{
|
|
||||||
names.Push(current.Name);
|
|
||||||
current = current.ContainingType;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 将栈中的名称用点号连接,形成完整的类名
|
|
||||||
return string.Join(".", names);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取命名类型符号的命名空间名称
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="symbol">要获取命名空间的命名类型符号</param>
|
|
||||||
/// <returns>命名空间名称,如果是全局命名空间则返回null</returns>
|
|
||||||
private static string? GetNamespace(this INamedTypeSymbol symbol)
|
|
||||||
{
|
|
||||||
return symbol.ContainingNamespace.IsGlobalNamespace
|
|
||||||
? null
|
|
||||||
: symbol.ContainingNamespace.ToDisplayString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 根据类型种类解析对应的类型关键字
|
/// 根据类型种类解析对应的类型关键字
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="symbol">要解析类型的命名类型符号</param>
|
/// <param name="symbol">要解析类型的命名类型符号</param>
|
||||||
/// <returns>对应类型的字符串表示,如"class"、"struct"或"record"</returns>
|
/// <returns>对应类型的字符串表示,如"class"、"struct"或"record"</returns>
|
||||||
private static string ResolveTypeKind(this INamedTypeSymbol symbol)
|
public static string ResolveTypeKind(this INamedTypeSymbol symbol)
|
||||||
{
|
{
|
||||||
return symbol.TypeKind switch
|
return symbol.TypeKind switch
|
||||||
{
|
{
|
||||||
@ -58,4 +27,82 @@ public static class INamedTypeSymbolExtensions
|
|||||||
_ => "class",
|
_ => "class",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 解析泛型信息,包括泛型参数和约束条件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="symbol">要解析泛型信息的命名类型符号</param>
|
||||||
|
/// <returns>包含泛型参数和约束条件的GenericInfo对象</returns>
|
||||||
|
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<string>()
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
return new GenericInfo(parameters, constraints);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构建单个类型参数的约束条件字符串
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tp">类型参数符号</param>
|
||||||
|
/// <returns>约束条件的字符串表示,如果没有约束则返回null</returns>
|
||||||
|
private static string? BuildConstraint(ITypeParameterSymbol tp)
|
||||||
|
{
|
||||||
|
var parts = new List<string>();
|
||||||
|
|
||||||
|
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)}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <param name="symbol">要获取完整类名的命名类型符号</param>
|
||||||
|
extension(INamedTypeSymbol symbol)
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 获取命名类型符号的完整类名(包括嵌套类型名称)
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>完整的类名,格式为"外层类名.内层类名.当前类名"</returns>
|
||||||
|
public string GetFullClassName()
|
||||||
|
{
|
||||||
|
var names = new Stack<string>();
|
||||||
|
var current = symbol;
|
||||||
|
|
||||||
|
// 遍历包含类型链,将所有类型名称压入栈中
|
||||||
|
while (current != null)
|
||||||
|
{
|
||||||
|
names.Push(current.Name);
|
||||||
|
current = current.ContainingType;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将栈中的名称用点号连接,形成完整的类名
|
||||||
|
return string.Join(".", names);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取命名类型符号的命名空间名称
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>命名空间名称,如果是全局命名空间则返回null</returns>
|
||||||
|
public string? GetNamespace()
|
||||||
|
{
|
||||||
|
return symbol.ContainingNamespace.IsGlobalNamespace
|
||||||
|
? null
|
||||||
|
: symbol.ContainingNamespace.ToDisplayString();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
13
GFramework.SourceGenerators.Common/info/GenericInfo.cs
Normal file
13
GFramework.SourceGenerators.Common/info/GenericInfo.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace GFramework.SourceGenerators.Common.info;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 表示泛型信息的数据结构
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="Parameters">泛型参数字符串</param>
|
||||||
|
/// <param name="Constraints">泛型约束列表</param>
|
||||||
|
public record struct GenericInfo(
|
||||||
|
string Parameters,
|
||||||
|
IReadOnlyList<string> Constraints
|
||||||
|
);
|
||||||
@ -1,6 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using GFramework.SourceGenerators.Abstractions.logging;
|
using GFramework.SourceGenerators.Abstractions.logging;
|
||||||
using GFramework.SourceGenerators.Common.constants;
|
using GFramework.SourceGenerators.Common.constants;
|
||||||
@ -56,84 +54,42 @@ public sealed class LoggerGenerator : TypeAttributeClassGeneratorBase
|
|||||||
INamedTypeSymbol symbol,
|
INamedTypeSymbol symbol,
|
||||||
AttributeData attr)
|
AttributeData attr)
|
||||||
{
|
{
|
||||||
var ns = symbol.ContainingNamespace.IsGlobalNamespace
|
var ns = symbol.GetNamespace();
|
||||||
? null
|
|
||||||
: symbol.ContainingNamespace.ToDisplayString();
|
|
||||||
|
|
||||||
// 类名和嵌套类完整路径
|
|
||||||
var className = symbol.GetFullClassName();
|
var className = symbol.GetFullClassName();
|
||||||
|
var typeKind = symbol.ResolveTypeKind();
|
||||||
|
var generics = symbol.ResolveGenerics();
|
||||||
|
|
||||||
// 构造函数参数解析
|
var logName = attr.GetFirstCtorString(className);
|
||||||
var logName = className;
|
var fieldName = attr.GetNamedArgument("FieldName", "_log");
|
||||||
if (attr.ConstructorArguments.Length > 0)
|
var access = attr.GetNamedArgument("AccessModifier", "private");
|
||||||
{
|
var isStatic = attr.GetNamedArgument("IsStatic", true);
|
||||||
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 staticKeyword = isStatic ? "static " : "";
|
var staticKeyword = isStatic ? "static " : "";
|
||||||
|
|
||||||
// 泛型参数
|
var sb = new StringBuilder()
|
||||||
var typeParams = symbol.TypeParameters.Length > 0
|
.AppendLine("// <auto-generated />")
|
||||||
? "<" + string.Join(", ", symbol.TypeParameters.Select(tp => tp.Name)) + ">"
|
.AppendLine($"using {PathContests.CoreAbstractionsNamespace}.logging;")
|
||||||
: "";
|
.AppendLine($"using {PathContests.CoreNamespace}.logging;");
|
||||||
|
|
||||||
// 泛型约束
|
|
||||||
var constraints = symbol.TypeParameters
|
|
||||||
.Where(tp => tp.ConstraintTypes.Length > 0 || tp.HasReferenceTypeConstraint || tp.HasValueTypeConstraint ||
|
|
||||||
tp.HasConstructorConstraint)
|
|
||||||
.Select(tp =>
|
|
||||||
{
|
|
||||||
var parts = new List<string>();
|
|
||||||
|
|
||||||
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("// <auto-generated />");
|
|
||||||
sb.AppendLine($"using {PathContests.CoreAbstractionsNamespace}.logging;");
|
|
||||||
sb.AppendLine($"using {PathContests.CoreNamespace}.logging;");
|
|
||||||
|
|
||||||
if (ns is not null)
|
if (ns is not null)
|
||||||
{
|
{
|
||||||
sb.AppendLine($"namespace {ns};");
|
sb.AppendLine()
|
||||||
sb.AppendLine();
|
.AppendLine($"namespace {ns};");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 生成 partial 类
|
sb.AppendLine()
|
||||||
sb.AppendLine($"partial {typeKind} {className}{typeParams}");
|
.AppendLine($"partial {typeKind} {className}{generics.Parameters}");
|
||||||
foreach (var c in constraints)
|
|
||||||
sb.AppendLine($" {c}");
|
|
||||||
sb.AppendLine("{");
|
|
||||||
sb.AppendLine($" /// <summary>Auto-generated logger</summary>");
|
|
||||||
sb.AppendLine(
|
|
||||||
$" {access} {staticKeyword}readonly ILogger {fieldName} = LoggerFactoryResolver.Provider.CreateLogger(\"{logName}\");");
|
|
||||||
sb.AppendLine("}");
|
|
||||||
|
|
||||||
return sb.ToString().TrimEnd();
|
foreach (var c in generics.Constraints)
|
||||||
|
sb.AppendLine($" {c}");
|
||||||
|
|
||||||
|
sb.AppendLine("{")
|
||||||
|
.AppendLine(" /// <summary>Auto-generated logger</summary>")
|
||||||
|
.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";
|
return $"{symbol.Name}.Logger.g.cs";
|
||||||
}
|
}
|
||||||
|
|
||||||
private static object? GetNamedArg(AttributeData attr, string name)
|
|
||||||
{
|
|
||||||
return attr.NamedArguments.FirstOrDefault(kv => kv.Key == name).Value.Value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user