Loading

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

书接上文

使用 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

posted @ 2024-11-15 21:12  J.晒太阳的猫  阅读(27)  评论(0编辑  收藏  举报