zoukankan      html  css  js  c++  java
  • .NET初探源代码生成(Source Generators)

    前言

    Source Generators顾名思义代码生成器,可进行创建编译时代码,也就是所谓的编译时元编程,这可让一些运行时映射的代码改为编译时,同样也加快了速度,我们可避免那种昂贵的开销,这是有价值的。

    实现ISourceGenerator

    集成ISourceGenerator接口,实现接口用于代码生成策略,它的生命周期由编译器控制,它可以在编译时创建时创建并且添加到编译中的代码,它为我们提供了编译时元编程,从而使我们对C#代码或者非C#源文件进行内部的检查。

        [Generator]
        class CustomGenerator: ISourceGenerator
        {
            public void Initialize(GeneratorInitializationContext context)
            {
                throw new NotImplementedException();
            }
    
            public void Execute(GeneratorExecutionContext context)
            {
                throw new NotImplementedException();
            }
        }
    
     public void Execute(GeneratorExecutionContext context)
    {
        var compilation = context.Compilation;
        var simpleCustomInterfaceSymbol = compilation.GetTypeByMetadataName("SourceGeneratorDemo.ICustom");
    
        const string code = @"
    using System;
    namespace SourceGeneratorDemo
    {   
        public partial class Program{
                 public static void Display(){
                 Console.WriteLine(""Hello World!"");
                }
        }
    }";
    
                if (!(context.SyntaxReceiver is CustomSyntaxReceiver receiver))
                    return;
                //语法树可参考Roslyn Docs
                SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(code);
    
                //context.AddSource("a.class", code);
                context.AddSource("a.class", SourceText.From(text: code, encoding: Encoding.UTF8));
    
                //https://github.com/gfoidl-Tests/SourceGeneratorTest
                {
                    StringBuilder sb = new();
                    sb.Append(@"using System;
    using System.Runtime.CompilerServices;
    #nullable enable
    [CompilerGenerated]
    public static class ExportDumper
    {
        public static void Dump()
        {");
                    foreach (BaseTypeDeclarationSyntax tds in receiver.Syntaxes)
                    {
                        sb.Append($@"
            Console.WriteLine(""type: {GetType(tds)}	name: {tds.Identifier}	file: {Path.GetFileName(tds.SyntaxTree.FilePath)}"");");
                    }
                    sb.AppendLine(@"
        }
    }");
    
                    SourceText sourceText = SourceText.From(sb.ToString(), Encoding.UTF8);
                    context.AddSource("DumpExports.generated", sourceText);
    
                    static string GetType(BaseTypeDeclarationSyntax tds) => tds switch
                    {
                        ClassDeclarationSyntax => "class",
                        RecordDeclarationSyntax => "record",
                        StructDeclarationSyntax => "struct",
                        _ => "-"
                    };
                }
            }
    

    context.AddSource字符串形式的源码添加到编译中,SourceText原文本抽象类,SourceText.From静态方法,Code指定要创建的源码内容,Encoding设置保存的编码格式,默认为UTF8,context.SyntaxReceiver可以获取在初始化期间注册的ISyntaxReceiver,获取创建的实例

    实现ISyntaxReceiver

    ISyntaxReceiver接口作为一个语法收集器的定义,可实现该接口,通过对该接口的实现,可过滤生成器所需

    public class CustomSyntaxReceiver : ISyntaxReceiver
    {
        public List<BaseTypeDeclarationSyntax> Syntaxes { get; } = new();
    
        /// <summary>
        ///     访问语法树
        /// </summary>
        /// <param name="syntaxNode"></param>
        public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
        {
            if (syntaxNode is BaseTypeDeclarationSyntax cds)
            {
                Syntaxes.Add(cds);
            }
        }
    }
    

    可以再此处进行过滤,如通过ClassDeclarationSyntax过滤Class类,当然也可以改为BaseTypeDeclarationSyntax,或者也可以使用InterfaceDeclarationSyntax添加接口类等等

    调试

    对于Source Generator可以通过添加Debugger.Launch()的形式进行对编译时的生成器进行调试,可以通过它很便捷的一步步调试代码.

     public void Initialize(GeneratorInitializationContext context)
            {
                Debugger.Launch();
                ......
            }
    

    Library&Properties

     <ItemGroup>
        <ProjectReference Include="..ClassLibrary1ClassLibrary1.csproj"
                          OutputItemType="Analyzer"
                          ReferenceOutputAssembly="false" />
      </ItemGroup>
    
    • ReferenceOutputAssembly:如果设置为false,则不包括引用项目的输出作为此项目的引用,但仍可确保在此项目之前生成其他项目。 默认为 true。
    • OutputItemType:可选项,将目标要输出的类型,默认为空,如果引用值设置为true(默认值),那么目标输出将为编译器的引用。

    Reference

    https://github.com/hueifeng/BlogSample/tree/master/src/SourceGeneratorDemo

  • 相关阅读:
    asp.net 2.0 treeview控件“简单”操纵
    http://www.cnblogs.com/Terrylee/archive/2006/11/12/aspnet_ajax_quickstarts.html(ajax入门 )
    JS实面treeview中选中父节点,子节点也选中
    系统地学习ASP.NET AJAX(5) 客户端脚本编程(Sys.UI命名空间下的类和快捷方法)(转)
    图例显示投标数据
    怎么用正则表达式判断数字(包括正,负,小数。。)
    treeview常见用法
    TreeView小结
    sql split分隔
    WCF 漫谈
  • 原文地址:https://www.cnblogs.com/yyfh/p/14545758.html
Copyright © 2011-2022 走看看