mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-22 10:34:30 +08:00
refactor(source-generators): 重构枚举扩展生成器和日志生成器
- 将 EnumExtensionsGenerator 从 IIncrementalGenerator 迁移到 AttributeClassGeneratorBase - 将 LoggerGenerator 从 IIncrementalGenerator 迁移到 AttributeClassGeneratorBase - 添加 AttributeEnumGeneratorBase 基类用于枚举相关生成器 - 更新依赖引用路径,使用新的抽象层和通用生成器基类 - 改进代码生成逻辑,使用强类型 Attribute 替代字符串匹配 - 添加详细的 XML 文档注释 - 修改项目目标框架为多版本支持 (net8.0;net9.0;net10.0)
This commit is contained in:
parent
e4a317b743
commit
4172952b11
@ -0,0 +1,59 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
|
using Microsoft.CodeAnalysis.Text;
|
||||||
|
|
||||||
|
namespace GFramework.SourceGenerators.Common.generator;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 基于特性的枚举生成器基类
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TAttribute">特性类型,必须继承自Attribute</typeparam>
|
||||||
|
public abstract class AttributeEnumGeneratorBase<TAttribute> : IIncrementalGenerator
|
||||||
|
where TAttribute : Attribute
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 获取特性的短名称(不包含后缀)
|
||||||
|
/// </summary>
|
||||||
|
protected abstract string AttributeShortNameWithoutSuffix { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 初始化增量生成器
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">增量生成器初始化上下文</param>
|
||||||
|
public void Initialize(IncrementalGeneratorInitializationContext context)
|
||||||
|
{
|
||||||
|
// 查找带有指定特性的枚举声明
|
||||||
|
var enums = context.SyntaxProvider.CreateSyntaxProvider(
|
||||||
|
(node, _) => node is EnumDeclarationSyntax decl &&
|
||||||
|
decl.AttributeLists.SelectMany(a => a.Attributes)
|
||||||
|
.Any(a => a.Name.ToString().Contains(AttributeShortNameWithoutSuffix)),
|
||||||
|
(ctx, _) =>
|
||||||
|
{
|
||||||
|
var decl = (EnumDeclarationSyntax)ctx.Node;
|
||||||
|
var symbol = ctx.SemanticModel.GetDeclaredSymbol(decl) as INamedTypeSymbol;
|
||||||
|
return (decl, symbol);
|
||||||
|
}).Where(x => x.symbol != null);
|
||||||
|
|
||||||
|
// 注册源代码输出
|
||||||
|
context.RegisterSourceOutput(enums, (spc, pair) =>
|
||||||
|
{
|
||||||
|
var attr = pair.symbol!.GetAttributes()
|
||||||
|
.FirstOrDefault(a => a.AttributeClass?.ToDisplayString() == typeof(TAttribute).FullName);
|
||||||
|
if (attr == null) return;
|
||||||
|
|
||||||
|
var src = Generate(pair.symbol!, attr);
|
||||||
|
spc.AddSource($"{pair.symbol!.Name}.EnumExtensions.g.cs", SourceText.From(src, Encoding.UTF8));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 生成源代码
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="enumSymbol">枚举符号</param>
|
||||||
|
/// <param name="attr">特性数据</param>
|
||||||
|
/// <returns>生成的源代码字符串</returns>
|
||||||
|
protected abstract string Generate(INamedTypeSymbol enumSymbol, AttributeData attr);
|
||||||
|
}
|
||||||
@ -3,7 +3,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFrameworks>net8.0;net9.0;net10.0</TargetFrameworks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,80 +1,82 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using GFramework.SourceGenerators.constants;
|
using GFramework.SourceGenerators.Abstractions.enums;
|
||||||
|
using GFramework.SourceGenerators.Common.diagnostics;
|
||||||
|
using GFramework.SourceGenerators.Common.generator;
|
||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
using Microsoft.CodeAnalysis.Text;
|
|
||||||
|
|
||||||
namespace GFramework.SourceGenerators.enums;
|
namespace GFramework.SourceGenerators.enums;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 枚举扩展方法生成器,用于自动生成枚举相关的扩展方法
|
||||||
|
/// </summary>
|
||||||
[Generator]
|
[Generator]
|
||||||
public class EnumExtensionsGenerator : IIncrementalGenerator
|
public sealed class EnumExtensionsGenerator : AttributeClassGeneratorBase
|
||||||
{
|
{
|
||||||
private const string AttributeFullName =
|
/// <summary>
|
||||||
$"{PathContests.RootAbstractionsPath}.enums.GenerateEnumExtensionsAttribute";
|
/// 使用强类型 Attribute,替代字符串
|
||||||
|
/// </summary>
|
||||||
|
protected override Type AttributeType =>
|
||||||
|
typeof(GenerateEnumExtensionsAttribute);
|
||||||
|
|
||||||
public void Initialize(IncrementalGeneratorInitializationContext context)
|
/// <summary>
|
||||||
|
/// 仅用于 Syntax 粗筛选
|
||||||
|
/// </summary>
|
||||||
|
protected override string AttributeShortNameWithoutSuffix => "GenerateEnumExtensions";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证符号是否为有效的枚举类型
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">源生产上下文</param>
|
||||||
|
/// <param name="syntax">类声明语法节点</param>
|
||||||
|
/// <param name="symbol">命名类型符号</param>
|
||||||
|
/// <param name="attr">属性数据</param>
|
||||||
|
/// <returns>验证是否通过</returns>
|
||||||
|
protected override bool ValidateSymbol(
|
||||||
|
SourceProductionContext context,
|
||||||
|
ClassDeclarationSyntax syntax,
|
||||||
|
INamedTypeSymbol symbol,
|
||||||
|
AttributeData attr)
|
||||||
{
|
{
|
||||||
// 1. 找到所有 EnumDeclarationSyntax 节点
|
if (symbol.TypeKind != TypeKind.Enum)
|
||||||
var enumDecls = context.SyntaxProvider
|
|
||||||
.CreateSyntaxProvider(
|
|
||||||
(s, _) => s is EnumDeclarationSyntax,
|
|
||||||
(ctx, _) =>
|
|
||||||
(EnumDecl: (EnumDeclarationSyntax)ctx.Node, ctx.SemanticModel))
|
|
||||||
.Where(t => t.EnumDecl != null);
|
|
||||||
|
|
||||||
// 2. 解析为 symbol 并过滤带 Attribute 的 enum
|
|
||||||
var enumSymbols = enumDecls
|
|
||||||
.Select((t, _) =>
|
|
||||||
{
|
|
||||||
var model = t.SemanticModel;
|
|
||||||
var enumDecl = t.EnumDecl;
|
|
||||||
var symbol = model.GetDeclaredSymbol(enumDecl) as INamedTypeSymbol;
|
|
||||||
return symbol;
|
|
||||||
})
|
|
||||||
.Where(symbol => symbol != null)
|
|
||||||
.Select((symbol, _) =>
|
|
||||||
{
|
|
||||||
var hasAttr = symbol!.GetAttributes().Any(ad =>
|
|
||||||
ad.AttributeClass?.ToDisplayString() == AttributeFullName);
|
|
||||||
return (Symbol: symbol, HasAttr: hasAttr);
|
|
||||||
})
|
|
||||||
.Where(x => x.HasAttr)
|
|
||||||
.Collect();
|
|
||||||
|
|
||||||
// 3. 为每个 enum 生成代码
|
|
||||||
context.RegisterSourceOutput(enumSymbols, (spc, list) =>
|
|
||||||
{
|
{
|
||||||
foreach (var enumSymbol in list.Select(item => item.Symbol))
|
var loc = syntax.Identifier.GetLocation();
|
||||||
try
|
context.ReportDiagnostic(Diagnostic.Create(
|
||||||
{
|
CommonDiagnostics.ClassMustBePartial, // 可以定义一个新的 Enum 专用 Diagnostic
|
||||||
var src = GenerateForEnum(enumSymbol);
|
loc,
|
||||||
var hintName = $"{enumSymbol.Name}.EnumExtensions.g.cs";
|
symbol.Name
|
||||||
spc.AddSource(hintName, SourceText.From(src, Encoding.UTF8));
|
));
|
||||||
}
|
return false;
|
||||||
catch (Exception ex)
|
}
|
||||||
{
|
|
||||||
// 发生异常时生成一个注释文件(避免完全静默失败)
|
return true;
|
||||||
var err = $"// EnumExtensionsGenerator failed for {enumSymbol?.Name}: {ex.Message}";
|
|
||||||
spc.AddSource($"{enumSymbol?.Name}.EnumExtensions.Error.g.cs",
|
|
||||||
SourceText.From(err, Encoding.UTF8));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GenerateForEnum(INamedTypeSymbol enumSymbol)
|
/// <summary>
|
||||||
|
/// 生成枚举扩展方法的源代码
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="symbol">枚举类型符号</param>
|
||||||
|
/// <param name="attr">属性数据</param>
|
||||||
|
/// <returns>生成的源代码字符串</returns>
|
||||||
|
protected override string Generate(INamedTypeSymbol symbol, AttributeData attr)
|
||||||
{
|
{
|
||||||
var ns = enumSymbol.ContainingNamespace.IsGlobalNamespace
|
var ns = symbol.ContainingNamespace.IsGlobalNamespace
|
||||||
? null
|
? null
|
||||||
: enumSymbol.ContainingNamespace.ToDisplayString();
|
: symbol.ContainingNamespace.ToDisplayString();
|
||||||
var enumName = enumSymbol.Name;
|
|
||||||
var fullEnumName = enumSymbol.ToDisplayString(); // 包含命名空间
|
var enumName = symbol.Name;
|
||||||
var members = enumSymbol.GetMembers().OfType<IFieldSymbol>().Where(f => f.ConstantValue != null).ToArray();
|
var fullEnumName = symbol.ToDisplayString();
|
||||||
|
var members = symbol.GetMembers()
|
||||||
|
.OfType<IFieldSymbol>()
|
||||||
|
.Where(f => f.ConstantValue != null)
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
sb.AppendLine("// <auto-generated />");
|
sb.AppendLine("// <auto-generated />");
|
||||||
sb.AppendLine("using System;");
|
sb.AppendLine("using System;");
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(ns))
|
if (!string.IsNullOrEmpty(ns))
|
||||||
{
|
{
|
||||||
sb.AppendLine($"namespace {ns}");
|
sb.AppendLine($"namespace {ns}");
|
||||||
@ -89,25 +91,18 @@ public class EnumExtensionsGenerator : IIncrementalGenerator
|
|||||||
sb.AppendLine($" public static partial class {enumName}Extensions");
|
sb.AppendLine($" public static partial class {enumName}Extensions");
|
||||||
sb.AppendLine(" {");
|
sb.AppendLine(" {");
|
||||||
|
|
||||||
// 1. 单项 IsX 方法
|
// 生成 IsX 方法
|
||||||
// 替换原第93行开始的 foreach 块
|
foreach (var memberName in members.Select(m => m.Name))
|
||||||
var memberChecks = members.Select(m =>
|
|
||||||
{
|
{
|
||||||
var memberName = m.Name;
|
sb.AppendLine($" /// <summary>Auto-generated: 是否为 {memberName}</summary>");
|
||||||
var safeMethodName = $"Is{memberName}";
|
sb.AppendLine(
|
||||||
return $@" /// <summary>Auto-generated: 是否为 {memberName}</summary>
|
$" public static bool Is{memberName}(this {fullEnumName} value) => value == {fullEnumName}.{memberName};");
|
||||||
public static bool {safeMethodName}(this {fullEnumName} value) => value == {fullEnumName}.{memberName};
|
sb.AppendLine();
|
||||||
|
}
|
||||||
|
|
||||||
";
|
// 生成 IsIn 方法
|
||||||
}).ToArray();
|
|
||||||
|
|
||||||
sb.Append(string.Join("", memberChecks));
|
|
||||||
|
|
||||||
|
|
||||||
// 2. IsIn(params ...) 方法
|
|
||||||
sb.AppendLine(" /// <summary>Auto-generated: 判断是否属于指定集合</summary>");
|
sb.AppendLine(" /// <summary>Auto-generated: 判断是否属于指定集合</summary>");
|
||||||
sb.AppendLine(
|
sb.AppendLine($" public static bool IsIn(this {fullEnumName} value, params {fullEnumName}[] values)");
|
||||||
$" public static bool IsIn(this {fullEnumName} value, params {fullEnumName}[] values)");
|
|
||||||
sb.AppendLine(" {");
|
sb.AppendLine(" {");
|
||||||
sb.AppendLine(" if (values == null) return false;");
|
sb.AppendLine(" if (values == null) return false;");
|
||||||
sb.AppendLine(" foreach (var v in values) if (value == v) return true;");
|
sb.AppendLine(" foreach (var v in values) if (value == v) return true;");
|
||||||
@ -119,4 +114,12 @@ public class EnumExtensionsGenerator : IIncrementalGenerator
|
|||||||
|
|
||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取生成文件的提示名称
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="symbol">命名类型符号</param>
|
||||||
|
/// <returns>生成文件的提示名称</returns>
|
||||||
|
protected override string GetHintName(INamedTypeSymbol symbol)
|
||||||
|
=> $"{symbol.Name}.EnumExtensions.g.cs";
|
||||||
}
|
}
|
||||||
@ -1,12 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using GFramework.SourceGenerators.Common.diagnostics;
|
using GFramework.SourceGenerators.Abstractions.logging;
|
||||||
using GFramework.SourceGenerators.constants;
|
using GFramework.SourceGenerators.Common.generator;
|
||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
using Microsoft.CodeAnalysis.CSharp;
|
|
||||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
using Microsoft.CodeAnalysis.Text;
|
|
||||||
|
|
||||||
namespace GFramework.SourceGenerators.logging;
|
namespace GFramework.SourceGenerators.logging;
|
||||||
|
|
||||||
@ -14,171 +12,85 @@ namespace GFramework.SourceGenerators.logging;
|
|||||||
/// 日志生成器,用于为标记了LogAttribute的类自动生成日志字段
|
/// 日志生成器,用于为标记了LogAttribute的类自动生成日志字段
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Generator]
|
[Generator]
|
||||||
public sealed class LoggerGenerator : IIncrementalGenerator
|
public sealed class LoggerGenerator : AttributeClassGeneratorBase
|
||||||
{
|
{
|
||||||
private const string AttributeMetadataName = $"{PathContests.RootAbstractionsPath}.logging.LogAttribute";
|
/// <summary>
|
||||||
private const string AttributeShortName = "LogAttribute";
|
/// 强类型 Attribute
|
||||||
private const string AttributeShortNameWithoutSuffix = "Log";
|
/// </summary>
|
||||||
|
protected override Type AttributeType => typeof(LogAttribute);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 初始化生成器,设置语法过滤和代码生成逻辑
|
/// 用于语法快速筛选
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">增量生成器初始化上下文</param>
|
protected override string AttributeShortNameWithoutSuffix => "Log";
|
||||||
public void Initialize(IncrementalGeneratorInitializationContext context)
|
|
||||||
|
/// <summary>
|
||||||
|
/// 对类进行额外语义校验(可选)
|
||||||
|
/// </summary>
|
||||||
|
protected override bool ValidateSymbol(
|
||||||
|
SourceProductionContext context,
|
||||||
|
ClassDeclarationSyntax syntax,
|
||||||
|
INamedTypeSymbol symbol,
|
||||||
|
AttributeData attr)
|
||||||
{
|
{
|
||||||
// 1. 语法过滤:快速筛选候选类
|
// 可以加自定义规则,比如确保类是 public 或实现某接口
|
||||||
var targets = context.SyntaxProvider.CreateSyntaxProvider(
|
return true;
|
||||||
static (node, _) =>
|
|
||||||
{
|
|
||||||
if (node is not ClassDeclarationSyntax cls) return false;
|
|
||||||
// 只要包含 Log 字眼的 Attribute 就先放行
|
|
||||||
return cls.AttributeLists.SelectMany(a => a.Attributes).Any(a =>
|
|
||||||
{
|
|
||||||
var name = a.Name.ToString();
|
|
||||||
// 简单的字符串匹配,防止错过别名情况
|
|
||||||
return name.Contains(AttributeShortNameWithoutSuffix);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
static (ctx, _) =>
|
|
||||||
{
|
|
||||||
var classDecl = (ClassDeclarationSyntax)ctx.Node;
|
|
||||||
var symbol = ctx.SemanticModel.GetDeclaredSymbol(classDecl);
|
|
||||||
return (ClassDecl: classDecl, Symbol: symbol);
|
|
||||||
})
|
|
||||||
.Where(x => x.Symbol is not null);
|
|
||||||
|
|
||||||
// 2. 生成代码
|
|
||||||
context.RegisterSourceOutput(targets, (spc, pair) =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var classDecl = pair.ClassDecl;
|
|
||||||
var classSymbol = pair.Symbol!;
|
|
||||||
|
|
||||||
// 再次确认是否真的含有目标 Attribute (语义检查)
|
|
||||||
var attr = GetAttribute(classSymbol);
|
|
||||||
if (attr == null) return; // 可能是名字相似但不是我们要的 Attribute
|
|
||||||
|
|
||||||
// 检查 partial
|
|
||||||
if (!classDecl.Modifiers.Any(SyntaxKind.PartialKeyword))
|
|
||||||
{
|
|
||||||
spc.ReportDiagnostic(Diagnostic.Create(
|
|
||||||
CommonDiagnostics.ClassMustBePartial,
|
|
||||||
classDecl.Identifier.GetLocation(),
|
|
||||||
classSymbol.Name));
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var source = Generate(classSymbol, attr);
|
|
||||||
var hintName = $"{classSymbol.Name}.Logger.g.cs";
|
|
||||||
spc.AddSource(hintName, SourceText.From(source, Encoding.UTF8));
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
// === 关键修复:生成错误报告文件 ===
|
|
||||||
var errorSource = $"// source generator error: {ex.Message}\n// StackTrace:\n// {ex.StackTrace}";
|
|
||||||
// 替换非法字符以防文件名报错
|
|
||||||
var safeName = pair.Symbol?.Name ?? "Unknown";
|
|
||||||
spc.AddSource($"{safeName}.Logger.Error.g.cs", SourceText.From(errorSource, Encoding.UTF8));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取类符号上的LogAttribute特性
|
/// 生成 Logger 字段的代码
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="classSymbol">类符号</param>
|
protected override string Generate(INamedTypeSymbol symbol, AttributeData attr)
|
||||||
/// <returns>LogAttribute特性数据,如果不存在则返回null</returns>
|
|
||||||
private static AttributeData? GetAttribute(INamedTypeSymbol classSymbol)
|
|
||||||
{
|
{
|
||||||
return classSymbol.GetAttributes().FirstOrDefault(a =>
|
var ns = symbol.ContainingNamespace.IsGlobalNamespace
|
||||||
{
|
|
||||||
var cls = a.AttributeClass;
|
|
||||||
if (cls == null) return false;
|
|
||||||
|
|
||||||
// 宽松匹配:全名匹配 OR 名字匹配
|
|
||||||
return cls.ToDisplayString() == AttributeMetadataName ||
|
|
||||||
cls.Name == AttributeShortName;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 生成日志字段代码
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="classSymbol">类符号</param>
|
|
||||||
/// <param name="attr">LogAttribute特性数据</param>
|
|
||||||
/// <returns>生成的C#代码字符串</returns>
|
|
||||||
private static string Generate(INamedTypeSymbol classSymbol, AttributeData attr)
|
|
||||||
{
|
|
||||||
var ns = classSymbol.ContainingNamespace.IsGlobalNamespace
|
|
||||||
? null
|
? null
|
||||||
: classSymbol.ContainingNamespace.ToDisplayString();
|
: symbol.ContainingNamespace.ToDisplayString();
|
||||||
|
|
||||||
var className = classSymbol.Name;
|
var className = symbol.Name;
|
||||||
|
|
||||||
// === 解析 Name ===
|
// 解析构造函数参数
|
||||||
var name = className; // 默认使用类名
|
var name = className;
|
||||||
|
|
||||||
// 检查是否有构造函数参数
|
|
||||||
if (attr.ConstructorArguments.Length > 0)
|
if (attr.ConstructorArguments.Length > 0)
|
||||||
{
|
{
|
||||||
var argValue = attr.ConstructorArguments[0].Value;
|
var argValue = attr.ConstructorArguments[0].Value;
|
||||||
|
name = argValue is string s && !string.IsNullOrWhiteSpace(s)
|
||||||
name = argValue switch
|
? s
|
||||||
{
|
: className;
|
||||||
// 情况 1: 参数存在,但值为 null (例如 [Log] 且构造函数有默认值 null)
|
|
||||||
null => className,
|
|
||||||
// 情况 2: 参数存在,且是有效的字符串 (例如 [Log("MyCategory")])
|
|
||||||
string s when !string.IsNullOrWhiteSpace(s) => s,
|
|
||||||
_ => $"{className}_InvalidArg"
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// === 解析 Named Arguments (更加安全的获取方式) ===
|
// 解析命名参数
|
||||||
var fieldName = GetNamedArg(attr, "FieldName")?.ToString() ?? "_log";
|
var fieldName = GetNamedArg(attr, "FieldName")?.ToString() ?? "_log";
|
||||||
var access =
|
var access = GetNamedArg(attr, "AccessModifier")?.ToString() ?? "private";
|
||||||
GetNamedArg(attr, "AccessModifier")?.ToString() ??
|
|
||||||
"private"; // 注意:如果你的 AccessModifier 是枚举,这里得到的可能是 int 或枚举名
|
|
||||||
|
|
||||||
// 处理 bool 类型
|
|
||||||
var isStaticObj = GetNamedArg(attr, "IsStatic");
|
var isStaticObj = GetNamedArg(attr, "IsStatic");
|
||||||
var isStatic = isStaticObj is not bool b || b; // 默认为 true
|
var isStatic = isStaticObj is not bool b || b; // 默认 true
|
||||||
|
|
||||||
var staticKeyword = isStatic ? "static " : "";
|
var staticKeyword = isStatic ? "static " : "";
|
||||||
|
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
sb.AppendLine("// <auto-generated />");
|
sb.AppendLine("// <auto-generated />");
|
||||||
sb.AppendLine("using GFramework.Core.logging;"); // 确保这里引用了 ILog 和 Log 类
|
sb.AppendLine("using GFramework.Core.logging;");
|
||||||
|
|
||||||
if (ns is not null)
|
if (ns is not null)
|
||||||
{
|
{
|
||||||
sb.AppendLine($"namespace {ns}");
|
sb.AppendLine($"namespace {ns};");
|
||||||
sb.AppendLine("{");
|
sb.AppendLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
sb.AppendLine($" public partial class {className}");
|
sb.AppendLine($"partial class {className}");
|
||||||
sb.AppendLine(" {");
|
sb.AppendLine("{");
|
||||||
sb.AppendLine(" /// <summary>Auto-generated logger</summary>");
|
sb.AppendLine(" /// <summary>Auto-generated logger</summary>");
|
||||||
sb.AppendLine(
|
sb.AppendLine(
|
||||||
$" {access} {staticKeyword}readonly ILogger {fieldName} = " +
|
$" {access} {staticKeyword}readonly ILogger {fieldName} = new ConsoleLoggerFactory().GetLogger(\"{name}\");");
|
||||||
$"new ConsoleLoggerFactory().GetLogger(\"{name}\");");
|
sb.AppendLine("}");
|
||||||
sb.AppendLine(" }");
|
|
||||||
|
|
||||||
if (ns is not null)
|
return sb.ToString().TrimEnd();
|
||||||
sb.AppendLine("}");
|
|
||||||
|
|
||||||
return sb.ToString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 从特性数据中获取命名参数的值
|
/// 可以自定义生成文件名
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="attr">特性数据</param>
|
protected override string GetHintName(INamedTypeSymbol symbol)
|
||||||
/// <param name="name">参数名称</param>
|
=> $"{symbol.Name}.Logger.g.cs";
|
||||||
/// <returns>参数值,如果不存在则返回null</returns>
|
|
||||||
private static object? GetNamedArg(AttributeData attr, string name)
|
private static object? GetNamedArg(AttributeData attr, string name)
|
||||||
{
|
=> attr.NamedArguments.FirstOrDefault(kv => kv.Key == name).Value.Value;
|
||||||
return (from kv in attr.NamedArguments where kv.Key == name select kv.Value.Value).FirstOrDefault();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user