东莞网站搭建找哪里,注册建筑劳务公司需要什么条件,cms免费开源,python可以写网页吗在阅读本文前需掌握源代码生成器相关知识C#源代码生成器深入讲解一
C#源代码生成器深入讲解二—增量生成器
源代码生成器有个非常大的弊病#xff0c;每次都会遍历所有的语法树来分析,这样就有个问题#xff0c;每次可能只修改了很少一部分或者只有很少一部分的代码需要分析…在阅读本文前需掌握源代码生成器相关知识C#源代码生成器深入讲解一
C#源代码生成器深入讲解二—增量生成器
源代码生成器有个非常大的弊病每次都会遍历所有的语法树来分析,这样就有个问题每次可能只修改了很少一部分或者只有很少一部分的代码需要分析而增量源代码生成器可以理解为在之前的工作上做了一个筛选的动作,通过自定义的条件来过滤语法树,并且缓存起来避免在没有做任何更改的情况下重复工作提高效率。
1 增量生成器初体验
增量生成器和源代码生成器基本方法相同只不过只需要一个Initialize方法
新建一个生成器项目
[Generator(LanguageNames.CSharp)]
public class GreetingIncrementalGenerator : IIncrementalGenerator
{//仅仅实现一个接口public void Initialize(IncrementalGeneratorInitializationContext context){//生成源代码的操作不会立即执行而是在编译时执行context.RegisterPostInitializationOutput(e {e.AddSource($GreetingIncrementalGenerator.g.cs, //加上这句话告知编译器这个文件是由源代码生成器生成的//防止编译器进行代码分析避免不必要的编译器警告//auto-generatednamespace GreetingTest;class GreetingIncrementalGreetingIncremental{public static void SayHello(string name){global::System.Console.WriteLine($Hello, World {name}!);}});});}
}使用增量生成器
GreetingIncrementalGreetingIncremental.SayHello(IncrementalGeneratorInitialization);2 使用CreateSyntaxProvider
上一章节中直接使用字符串进行了代码生成而没有进行语法分析语法分析可采用context.RegisterSourceOutput方法该方法具有两个参数
声明一个分部类和分布方法
namespace SourceGenerator2
{public static partial class GreetingUsePartialClass{public static partial void SayHelloTo2(string name);}
}新建一个类库也就是语法生成器项目
[Generator(LanguageNames.CSharp)]
public sealed class GreetingIncrementalGenerator : IIncrementalGenerator
{public void Initialize(IncrementalGeneratorInitializationContext context){context.RegisterSourceOutput(//CreateSyntaxProvider接受一个判断方法判断是否满足要求并返回一个语法树context.SyntaxProvider.CreateSyntaxProvider(NodePredicate,(gsc, _) (MethodDeclarationSyntax)gsc.Node), //第二个参数为上一步返回的经过判断的语法树(spc, method) {var type method.Ancestors().OfTypeTypeDeclarationSyntax().First();var typeName type.Identifier.ValueText;spc.AddSource(${typeName}.g.cs,$$//加上这句话告知编译器这个文件是由源代码生成器生成的//防止编译器进行代码分析避免不必要的编译器警告//auto-generated#nullable enablenamespace SourceGenerator2;partial class {{typeName}}{public static partial void SayHelloTo2(string name){global::System.Console.WriteLine($Hello, World {name}!);}});});}//判断分部方法是否满足要求private static bool NodePredicate(SyntaxNode node, CancellationToken _) node is MethodDeclarationSyntax{Identifier.ValueText: SayHelloTo2,Modifiers: var methodModifiers and not [],ReturnType: PredefinedTypeSyntax{Keyword.RawKind: (int)SyntaxKind.VoidKeyword},TypeParameterList: null,ParameterList.Parameters:[{Type: PredefinedTypeSyntax{Keyword.RawKind: (int)SyntaxKind.StringKeyword}}],Parent: ClassDeclarationSyntax{Modifiers: var typeModifiers and not []}} methodModifiers.Any(SyntaxKind.PartialKeyword) typeModifiers.Any(SyntaxKind.PartialKeyword) methodModifiers.Any(SyntaxKind.StaticKeyword);使用在主项目中输入
GreetingUsePartialClass.SayHelloTo2(SourceGenerator2); 3. 使用ForAttributeMetadataName
类似于C#源代码生成器深入讲解一中05章节所讲如果想要借助特性来使用代码生成器则可以使用ForAttributeMetadataName方法
在主项目中声明特性
namespace SourceGenerator2
{[AttributeUsage(AttributeTargets.Method)]public sealed class SayHello2Attribute:Attribute;
}在主项目中声明一个分部方法并标记特性
namespace SourceGenerator2
{public static partial class GreetingUsePartialClass{[SayHello2]public static partial void SayHelloToAttribute(string name);}
}建立代码生成器项目
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Xml.Linq;namespace SourceGenerator2.UseAttribute
{[Generator(LanguageNames.CSharp)]public class SourceGenerator2UseAttribute : IIncrementalGenerator{public void Initialize(IncrementalGeneratorInitializationContext context){#regioncontext.RegisterSourceOutput(//与上一章节中的区别是使用了ForAttributeWithMetadataName来创建Provider//RegisterSourceOutput第一个参数IncrementalValueProviderTSourcecontext.SyntaxProvider.ForAttributeWithMetadataName(SourceGenerator2.SayHello2Attribute,NodePredicate,static (gasc, _) gasc switch{{TargetNode: MethodDeclarationSyntax node,TargetSymbol: IMethodSymbol{Name: var methodName,TypeParameters: [],Parameters: [{ Type.SpecialType: SpecialType.System_String, Name: var parameterName }],ReturnsVoid: true,IsStatic: true,ContainingType:{Name: var typeName,ContainingNamespace: var namespace,TypeKind: var typeKind and (TypeKind.Class or TypeKind.Struct or TypeKind.Interface)}}} new GatheredData{MethodName methodName,ParameterName parameterName,TypeName typeName,Namespace namespace,TypeKind typeKind,Node node},_ null}//Collectcombine等方法可对Provider进行组合).Collect(),//RegisterSourceOutput第二个参数ActionSourceProductionContext, TSource(spc, data) {foreach (var item in data){if (item is null){continue;}var namespaceName item.Namespace.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);namespaceName namespaceName[global::.Length..];var typeKindString item.TypeKind switch{ TypeKind.Class class, TypeKind.Struct struct, TypeKind.Interface interface, _ throw new NotImplementedException() };spc.AddSource(${item.TypeName}.g.cs,$$// auto-generated/#nullable enablenamespace {{namespaceName}};partial {{typeKindString}} {{item.TypeName}}{{{item.Node.Modifiers}} void {{item.MethodName}}(string {{item.ParameterName}}) global::System.Console.WriteLine($Hello, {{{item.ParameterName}}}!);});}});#endregion}public bool NodePredicate(SyntaxNode node, CancellationToken token) node is MethodDeclarationSyntax{Modifiers: var modifiers and not [],Parent: TypeDeclarationSyntax { Modifiers: var typemodifiers and not [] }} modifiers.Any(SyntaxKind.PartialKeyword) typemodifiers.Any(SyntaxKind.PartialKeyword);}
}file class GatheredData
{public string MethodName { set; get; }public string ParameterName { set; get; }public string TypeName { set; get; }public INamespaceSymbol Namespace { set; get; }public TypeKind TypeKind { set; get; }public MethodDeclarationSyntax Node { set; get; }
}4. CompilationProvider和AdditionalTextsProvider
很多源代码生成器都是针对程序集的而不是针对某个类的所以使用CompilationProvider
[Generator(LanguageNames.CSharp)]
public class MyTupleGenerator : IIncrementalGenerator
{public void Initialize(IncrementalGeneratorInitializationContext context){//很多源代码生成器都是针对程序集的而不是针对某个类的所以使用CompilationProvider//因为CompilationProvider提供的功能有限本次要使用TextsProvider所以要使用Combine方法进行组合context.RegisterSourceOutput(context.CompilationProvider.Combine(context.AdditionalTextsProvider.Collect()), Output);}private void Output(SourceProductionContext spc, (Compilation, ImmutableArrayAdditionalText) pair){var (compilation, additionalFiles) pair;spc.AddSource(mytuble.g.cs,source...省略);}
}