You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用IIncrementalGenerator查找目标项目实现指定接口的类以生成工厂类

在Blazor中使用IIncrementalGenerator生成实现IEditPresenter的工厂类

要在IIncrementalGenerator中找到所有实现IEditPresenter的类,不能再用旧的全量遍历语法树方式,而是要利用增量生成管道完成筛选、符号绑定和信息提取。以下是完整的修改实现:

完整修改代码

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using System.Text;

[Generator]
public class MySourceGenerator : IIncrementalGenerator
{
    public readonly record struct PresentationFactoryToGenerate
    {
        public readonly string Name;
        public readonly string IdType;
        public readonly string EditorType;

        public PresentationFactoryToGenerate(string name, string editorType, string idType)
        {
            Name = name;
            EditorType = editorType;
            IdType = idType;
        }
    }

    public void Initialize(IncrementalGeneratorInitializationContext context)
    {
        // 1. 筛选非抽象、非静态的类声明语法节点
        var classDeclarations = context.SyntaxProvider
            .CreateSyntaxProvider(
                predicate: static (node, _) => node is ClassDeclarationSyntax classDecl
                    && !classDecl.Modifiers.Any(SyntaxKind.AbstractKeyword)
                    && !classDecl.Modifiers.Any(SyntaxKind.StaticKeyword),
                transform: static (ctx, _) => ctx.Node as ClassDeclarationSyntax)
            .Where(static classDecl => classDecl != null);

        // 2. 绑定语义符号,过滤出实现IEditPresenter`2的类
        var presenterSymbols = classDeclarations
            .SelectMany(static (classDecl, cancellationToken) =>
            {
                if (context.SemanticModel.GetDeclaredSymbol(classDecl, cancellationToken) is not INamedTypeSymbol classSymbol)
                    return Enumerable.Empty<INamedTypeSymbol>();

                // 替换为你实际的IEditPresenter命名空间
                var editPresenterInterface = classSymbol.Compilation.GetTypeByMetadataName("Blazr.App.Presentation.IEditPresenter`2");
                if (editPresenterInterface == null)
                    return Enumerable.Empty<INamedTypeSymbol>();

                // 检查类是否实现目标接口
                var implementsInterface = classSymbol.AllInterfaces.Any(iface =>
                    iface.ConstructedFrom.Equals(editPresenterInterface, SymbolEqualityComparer.Default));

                return implementsInterface ? new[] { classSymbol } : Enumerable.Empty<INamedTypeSymbol>();
            });

        // 3. 提取泛型参数和类名,转换为生成所需结构
        var presentersToGenerate = presenterSymbols
            .Select(static (classSymbol, cancellationToken) =>
            {
                var implementedInterface = classSymbol.AllInterfaces.First(iface =>
                    iface.ConstructedFrom.MetadataName == "IEditPresenter`2");

                var genericArgs = implementedInterface.TypeArguments;
                string editorType = genericArgs[0].ToDisplayString();
                string idType = genericArgs[1].ToDisplayString();

                return new PresentationFactoryToGenerate(classSymbol.Name, editorType, idType);
            });

        // 4. 注册增量输出,生成工厂类代码
        context.RegisterSourceOutput(presentersToGenerate, static (spc, item) =>
        {
            string factoryCode = BuildPresenterFactory(item);
            spc.AddSource(
                $"{item.Name}EditPresenterFactory.g.cs",
                SourceText.From(factoryCode, Encoding.UTF8));
        });
    }

    private string BuildPresenterFactory(PresentationFactoryToGenerate item)
    {
        // 修正硬编码的Presenter类型,使用动态传入的类名
        return $$"""
            namespace Blazr.App.Presentation;

            public class {{item.Name}}EditPresenterFactory
            {
                private readonly IServiceProvider _serviceProvider;

                public {{item.Name}}EditPresenterFactory(IServiceProvider serviceProvider)
                {
                    _serviceProvider = serviceProvider;
                }

                public async ValueTask<IEditPresenter<{{item.EditorType}}, {{item.IdType}}>> GetPresenterAsync({{item.IdType}} id)
                {
                    var presenter = ActivatorUtilities.CreateInstance<{{item.Name}}>(_serviceProvider);
                    ArgumentNullException.ThrowIfNull(presenter);

                    await presenter.LoadAsync(id);

                    return presenter;
                }
            }
            """;
    }
}

关键要点说明

  1. 增量管道设计:

    • 用SyntaxProvider.CreateSyntaxProvider只筛选目标类,减少无效计算,符合增量生成的性能要求。
    • 通过SemanticModel.GetDeclaredSymbol绑定语法节点到语义符号,确保能准确获取类的接口实现信息。
  2. 泛型接口匹配:

    • 泛型接口的元数据名称格式为接口名+泛型参数数量,比如IEditPresenter<TEditor, TId>对应IEditPresenter2`。
    • 使用SymbolEqualityComparer.Default做符号比较,避免因命名空间或别名导致的匹配错误。
  3. 输出逻辑修正:

    • 用RegisterSourceOutput替代旧的RegisterPostInitializationOutput,仅在目标类变化时重新生成对应工厂类,符合增量生成生命周期。
    • 移除硬编码的FarmEditPresenter,动态使用传入的类名,确保生成代码与目标Presenter类匹配。

内容的提问来源于stack exchange,提问作者MrC aka Shaun Curtis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 03:26:18