生成器项目
得基于.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,注意变更源生成器需要重新打开工程来刷新新生成的代码