首页 > 其他分享 >使用 roslyn 的 Source Generator 自动完成依赖收集和注册 - IIncrementalGenerator 番外

使用 roslyn 的 Source Generator 自动完成依赖收集和注册 - IIncrementalGenerator 番外

时间:2024-11-15 21:08:29浏览次数:1  
标签:return classDecl Generator 番外 System Source var using null

书接上文

使用 roslyn 的 Source Generator 自动完成依赖收集和注册 - J.晒太阳的猫 - 博客园

收到来自徳熙大佬的提示:

现在 VisualStudio 团队推荐使用增量的源代码生成器,因为现在这篇博客使用的源代码生成器让原本就卡慢的 Visual Studio 更加卡慢了。
新的增量源代码生成器是很好饯行不可变和增量模式的写法,可以使用更少的资源

尝试 IIncrementalGenerator 进行增量 Source Generator 生成代码 | 林德熙

以下是使用 IIncrementalGenerator 优化之后的代码(全程由 CHAT-GPT-o1-mini 支持)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;

namespace WpfAppTemplate1.Generators;

/*
 * 使用 IIncrementalGenerator 实现,优化 VS 调用性能
 */

[Generator]
internal class ViewDependencyInjectionGenerator2 : IIncrementalGenerator
{
    public void Initialize(IncrementalGeneratorInitializationContext context)
    {
        // 注册一个语法接收器,筛选出所有以 View 或 ViewModel 结尾的类声明
        var classDeclarations = context
            .SyntaxProvider.CreateSyntaxProvider(
                predicate: IsCandidateClass, // 先通过语法筛选
                transform: GetSemanticTarget // 再通过语义筛选
            )
            .Where(symbolAndClass => symbolAndClass.Symbol != null); // 过滤掉不符合条件

        // 收集所有符合条件的类的全名
        var classFullNames = classDeclarations
            .Select((symbolAndClass, ct) => symbolAndClass.Symbol!.ToDisplayString())
            .Collect();

        // 当收集完成后,进行代码的生成
        context.RegisterSourceOutput(
            classFullNames,
            (spc, fullNames) =>
            {
                if (fullNames.IsDefault || !fullNames.Any())
                {
                    // 如果没有符合条件的类,则不生成任何代码
                    return;
                }

                var sourceBuilder = new StringBuilder(
                    @"
using Microsoft.Extensions.DependencyInjection;

public static class ViewModelDependencyInjection
{
    public static void AddViewModelServices(this IServiceCollection services)
    {
"
                );

                // 使用 HashSet 来避免重复添加
                HashSet<string> added = new HashSet<string>();

                foreach (var fullName in fullNames.Distinct())
                {
                    if (added.Add(fullName))
                    {
                        sourceBuilder.AppendLine($"        services.AddSingleton<{fullName}>();");
                    }
                }

                sourceBuilder.AppendLine("    }");
                sourceBuilder.AppendLine("}");

                // 将生成的代码添加到编译过程中
                spc.AddSource(
                    "ViewModelDependencyInjection.g.cs",
                    SourceText.From(sourceBuilder.ToString(), Encoding.UTF8)
                );
            }
        );
    }

    /// <summary>
    /// 判断一个类声明是否是潜在的候选者(名称以 View 或 ViewModel 结尾)
    /// </summary>
    private static bool IsCandidateClass(SyntaxNode node, CancellationToken _)
    {
        return node is ClassDeclarationSyntax classDecl
            && (
                classDecl.Identifier.Text.EndsWith("View")
                || classDecl.Identifier.Text.EndsWith("ViewModel")
            );
    }

    /// <summary>
    /// 获取符合条件的类的符号信息
    /// </summary>
    private static (INamedTypeSymbol? Symbol, ClassDeclarationSyntax? ClassDecl) GetSemanticTarget(
        GeneratorSyntaxContext context,
        CancellationToken ct
    )
    {
        var classDecl = (ClassDeclarationSyntax)context.Node;

        var symbol = context.SemanticModel.GetDeclaredSymbol(classDecl) as INamedTypeSymbol;

        if (symbol == null)
            return (null, null);

        // 检查继承关系
        if (classDecl.Identifier.Text.EndsWith("ViewModel"))
        {
            // ViewModel 必须继承 Stylet.Screen
            if (!InheritsFrom(symbol, "Stylet.Screen"))
                return (null, null);
        }
        else if (classDecl.Identifier.Text.EndsWith("View"))
        {
            // View 必须继承 System.Windows.FrameworkElement
            if (!InheritsFrom(symbol, "System.Windows.FrameworkElement"))
                return (null, null);
        }
        else
        {
            return (null, null);
        }

        return (symbol, classDecl);
    }

    /// <summary>
    /// 判断一个符号是否继承自指定的基类
    /// </summary>
    private static bool InheritsFrom(INamedTypeSymbol typeSymbol, string baseClassFullName)
    {
        var current = typeSymbol.BaseType;
        while (current != null)
        {
            if (current.ToDisplayString() == baseClassFullName)
            {
                return true;
            }
            current = current.BaseType;
        }
        return false;
    }
}

原文链接:https://www.cnblogs.com/jasongrass/p/18548653

标签:return,classDecl,Generator,番外,System,Source,var,using,null
From: https://www.cnblogs.com/jasongrass/p/18548653

相关文章

  • source activate 和 conda activate 的区别和使用
    最近遇到一个奇怪的事情:直接分配的计算节点(salloc指令),ssh连接上之后应该是bash终端,只能用condaactivate但是用提交脚本的方式来分配计算节点(sbatch指令),脚本中如果要激活环境,却只能用sourceactivate。调研了一下:https://developer.baidu.com/article/details/27996......
  • unity3d————Resources异步加载
    知识点一:Resources异步加载是什么?在Unity中,资源加载可以分为同步加载和异步加载两种方式。同步加载会在主线程中直接进行,如果加载的资源过大,可能会导致程序卡顿,因为从硬盘读取数据到内存并进行处理是需要时间的。而异步加载则是Unity内部新开一个线程进行资源加载,这样就不会造......
  • 番外-JDBC:2024年最新java连接数据库教程
    前言JavaScript的内容晚点更新,今天继续更新一点番外,今天更新的是jdbc,如何用java连接数据库1.导包要使java能够连接数据库我们需要导入一个包,请按照以下操作安装并导包1.进入官网MySQL以上为官网链接进去后点击下载继续按图片点击 根据自己使用的语言选择,这里演示的......
  • Windows Resource Protection (WRP) 是微软引入的一项系统保护机制,旨在保护 Windows
    什么是WindowsResourceProtection(WRP)?WindowsResourceProtection(WRP)是微软引入的一项系统保护机制,旨在保护Windows操作系统中的关键资源,包括系统文件、驱动程序、注册表设置以及系统配置文件等。WRP是WindowsVista中首次引入的,它加强了对操作系统核心文件和资源的......
  • mybatis-generator使用
    Mybatis-generator使用一.添加依赖 <!--首先要有mybatis的依赖和数据库驱动--> <dependencies> <!--mybatis依赖--><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-......
  • 使用 roslyn 的 Source Generator 自动完成依赖收集和注册
    在使用Hosting构建WPF程序提到,因为不使用Stylet默认的IOC容器,所以不能自动收集和注册View/ViewModel,需要动手处理。如果项目比较大,手动处理显然过于麻烦。这里使用roslyn的SourceGenerator自动完成依赖收集和注册。源码JasonGrass/WpfAppTemplate1:WPF+Styl......
  • gcc 1.c和g++ 1.c编译阶段有什么区别?如何知道g++编译默认会定义_GNU_SOURCE?
    gcc1.c和g++1.c编译阶段有什么区别?借用 gcc1.c和g++1.c有什么区别? 的示例代码,以汇编代码为比较目标,再经过汇编,最后生成ELF文件,三个过程结果均无差异,这个阶段充分证明了c和c++是多么相似。编译到汇编gcc-S1.c-o1.sg++-S1.c-o11.s .file "1.c"......
  • 利用FreeSql.Generator自动根据数据库表动态生成实体类
    安装dotnettoolinstall-gFreeSql.Generator示例FreeSql.Generator-Razor1-NameOptions0,0,0,1-NameSpaceLinCms.Core.Entities-DB"MySql,DataSource=127.0.0.1;Port=3306;UserID=root;Password=123456;InitialCatalog=lincms;Charset=utf8;SslMode=none;M......
  • js 的generator函数是什么
    在JavaScript中,Generator函数(生成器函数)是一种特殊类型的函数,它可以暂停执行并且可以在后续的某个时刻恢复执行。与普通函数不同,Generator函数不会在调用时立即执行,而是返回一个Generator对象,你可以通过该对象控制函数的执行过程。1.如何定义一个Generator函数Generato......
  • Failed to load local image resource(在小程序中,`src` 属性需要使用双花括号 `{{ }}`
    文章目录修改WXML文件确保图像文件路径正确检查逻辑层代码总结[渲染层网络层错误]Failedtoloadlocalimageresource/components/antiFakeQuery/imageSrctheserverrespondedwithastatusof500(HTTP/1.1500InternalServerError)(env:Windows......