生成器项目
得基于.Net Stander 2.0
重要:<IsRoslynComponent>true</IsRoslynComponent>、<IncludeBuildOutput>false</IncludeBuildOutput>、 <PackageReference Include="Microsoft.CodeAnalysis" Version="4.14.0" />
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<IncludeBuildOutput>false</IncludeBuildOutput>
<LangVersion>latest</LangVersion>
<IsRoslynComponent>true</IsRoslynComponent>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis" Version="4.14.0" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.14.0" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\MasterNeverDown.SA\MasterNeverDown.SA.csproj" />
</ItemGroup>
</Project>
生成器示例
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
using System;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Text;
namespace LoggingGenerator
{
[Generator]
public class LoggingGenerator : IIncrementalGenerator
{
// 记录初始化过程
public LoggingGenerator()
{
System.Diagnostics.Debug.WriteLine("LoggingGenerator initialized");
}
public void Initialize(IncrementalGeneratorInitializationContext context)
{
//Debugger.Launch();
// 记录初始化开始
LogMessage("Initializing generator");
// 筛选出标记了[LogMethod]特性的方法
IncrementalValuesProvider<IMethodSymbol> methodsToLog = context.SyntaxProvider
.CreateSyntaxProvider(
predicate: (s, _) => IsMethodDeclarationWithAttribute(s),
transform: (ctx, _) => GetMethodSymbol(ctx))
.Where(m => m != null)!;
// 注册代码生成操作
context.RegisterSourceOutput(methodsToLog.Collect(),
(spc, methods) => GenerateLoggingCode(spc, methods));
// 记录初始化完成
LogMessage("Generator initialization completed");
}
private bool IsMethodDeclarationWithAttribute(SyntaxNode node)
{
// 检查是否为方法声明且有LogMethod特性
if (node is not Microsoft.CodeAnalysis.CSharp.Syntax.MethodDeclarationSyntax methodSyntax)
return false;
return methodSyntax.AttributeLists.Any(al =>
al.Attributes.Any(a => a.Name.ToString() == "LogMethod"));
}
private IMethodSymbol? GetMethodSymbol(GeneratorSyntaxContext context)
{
var methodSyntax = (Microsoft.CodeAnalysis.CSharp.Syntax.MethodDeclarationSyntax)context.Node;
return context.SemanticModel.GetDeclaredSymbol(methodSyntax) as IMethodSymbol;
}
private void GenerateLoggingCode(SourceProductionContext context, ImmutableArray<IMethodSymbol> methods)
{
// 记录代码生成开始
LogMessage($"Starting code generation for {methods.Length} methods");
foreach (var method in methods)
{
try
{
// 生成方法日志代码
var source = GenerateMethodLogging(method);
var hintName = $"{method.ContainingType.Name}_{method.Name}.g.cs";
context.AddSource(hintName, SourceText.From(source, Encoding.UTF8));
// 记录成功生成
LogMessage($"Generated logging code for {method.ContainingType.Name}.{method.Name}");
}
catch (Exception ex)
{
// 记录生成失败
LogError($"Failed to generate code for {method.ContainingType.Name}.{method.Name}: {ex.Message}");
}
}
// 记录代码生成完成
LogMessage("Code generation completed");
}
private string GenerateMethodLogging(IMethodSymbol method)
{
// 构建方法日志代码
var className = method.ContainingType.Name;
var methodName = method.Name;
var parameters = string.Join(", ", method.Parameters.Select(p => $"{p.Type} {p.Name}"));
var builder = new StringBuilder();
builder.AppendLine("// <auto-generated>");
builder.AppendLine("// This code was generated by a source generator.");
builder.AppendLine("// </auto-generated>");
builder.AppendLine();
builder.AppendLine($"namespace {method.ContainingNamespace.ToDisplayString()}");
builder.AppendLine("{");
builder.AppendLine($" public partial class {className}");
builder.AppendLine(" {");
builder.AppendLine($" partial void On{methodName}Executing({parameters});");
builder.AppendLine($" partial void On{methodName}Executing({parameters})");
builder.AppendLine(" {");
builder.AppendLine($" System.Diagnostics.Debug.WriteLine(\"Entering method {methodName}\");");
// 记录参数
foreach (var param in method.Parameters)
{
builder.AppendLine($" System.Diagnostics.Debug.WriteLine(\" Parameter {param.Name}: \" + ({param.Name}?.ToString() ?? \"null\"));");
}
builder.AppendLine(" }");
builder.AppendLine();
builder.AppendLine($" partial void On{methodName}Executed({parameters});");
builder.AppendLine($" partial void On{methodName}Executed({parameters})");
builder.AppendLine(" {");
builder.AppendLine($" System.Diagnostics.Debug.WriteLine(\"Exiting method {methodName}\");");
builder.AppendLine(" }");
builder.AppendLine(" }");
builder.AppendLine("}");
return builder.ToString();
}
// 日志方法 - 可根据需要调整输出方式
private void LogMessage(string message)
{
System.Diagnostics.Debug.WriteLine($"[LoggingGenerator] {DateTime.Now:yyyy-MM-dd HH:mm:ss} - {message}");
}
private void LogError(string message)
{
System.Diagnostics.Debug.WriteLine($"[LoggingGenerator ERROR] {DateTime.Now:yyyy-MM-dd HH:mm:ss} - {message}");
}
}
}
项目代码
重要: OutputItemType="Analyzer" ReferenceOutputAssembly="false"
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis" Version="4.14.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MasterNeverDown.SA\MasterNeverDown.SA.csproj" />
<ProjectReference Include="..\Sd\Sd\Sd.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>
<ItemGroup>
<Compilervisibility Include="PublicApiAnalyzer" Version="1.0.0" />
</ItemGroup>
</Project>
结果
在依赖项=》分析器=》项目生成g.cs,注意变更源生成器需要重新打开工程来刷新新生成的代码