生成器項目
得基于.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,注意變更源生成器需要重新打開工程來刷新新生成的代碼