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

如何用Source Generator实现StrongViewLocator简化注册方法重载

用Source Generator简化视图-视图模型无反射注册语法

现有需手动编写的无反射注册代码:

ViewLocator = new StrongViewLocator()
    .Register<MainWindowViewModel>(new ViewDefinition(typeof(MainWindow), () => new MainWindow()))
    .Register<AddTextDialogViewModel>(new ViewDefinition(typeof(AddTextDialog), () => new AddTextDialog()));

期望简化为更简洁的泛型链式调用:

ViewLocator = new StrongViewLocator()
    .Register<MainWindowViewModel, MainWindow>()
    .Register<AddTextDialogViewModel, AddTextDialog>();

以下是通过Source Generator实现该需求的具体步骤:


1. 定义泛型扩展方法占位

在主项目中定义一个空的泛型扩展方法,用于让编译器识别简化后的语法,实际逻辑由Source Generator生成:

namespace YourAppNamespace;

public static partial class StrongViewLocatorExtensions
{
    // 占位方法,仅提供语法提示,编译时会被生成代码替换
    public static StrongViewLocator Register<TVm, TView>(this StrongViewLocator locator)
    {
        throw new NotImplementedException("实际实现由Source Generator生成");
    }
}

2. 编写Source Generator

创建一个.NET Standard类库项目,引用Microsoft.CodeAnalysis.CSharp和Microsoft.CodeAnalysis.Analyzers NuGet包,实现ISourceGenerator接口:

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

namespace ViewLocatorGenerator;

[Generator]
public class ViewLocatorRegistrationGenerator : ISourceGenerator
{
    public void Initialize(GeneratorInitializationContext context)
    {
        // 注册语法接收器,筛选目标泛型方法调用
        context.RegisterForSyntaxNotifications(() => new ViewLocatorSyntaxReceiver());
    }

    public void Execute(GeneratorExecutionContext context)
    {
        if (context.SyntaxReceiver is not ViewLocatorSyntaxReceiver receiver)
            return;

        var semanticModel = context.Compilation;
        foreach (var invocation in receiver.MatchingInvocations)
        {
            // 解析语义信息,验证是否为StrongViewLocator的Register调用
            if (semanticModel.GetSemanticModel(invocation.SyntaxTree).GetSymbolInfo(invocation.Expression).Symbol is not IMethodSymbol methodSymbol)
                continue;
            if (methodSymbol.ReceiverType?.Name != "StrongViewLocator")
                continue;

            // 获取泛型参数(视图模型、视图类型)
            var typeArgs = methodSymbol.TypeArguments;
            if (typeArgs.Length != 2) continue;
            
            var vmTypeFullName = typeArgs[0].ToDisplayString();
            var viewTypeFullName = typeArgs[1].ToDisplayString();

            // 获取调用所在的命名空间
            var nsDeclaration = invocation.Ancestors().OfType<NamespaceDeclarationSyntax>().FirstOrDefault();
            var targetNamespace = nsDeclaration?.Name.ToString() ?? "YourAppNamespace";

            // 生成对应的扩展方法实现代码
            var sourceCode = new StringBuilder();
            sourceCode.Append($@"
namespace {targetNamespace};

public static partial class StrongViewLocatorExtensions
{{
    public static StrongViewLocator Register<{typeArgs[0].Name}, {typeArgs[1].Name}>(this StrongViewLocator locator)
    {{
        return locator.Register<{vmTypeFullName}>(new ViewDefinition(typeof({viewTypeFullName}), () => new {viewTypeFullName}()));
    }}
}}");

            // 将生成代码添加到编译上下文
            context.AddSource($"ViewLocator_{typeArgs[0].Name}_{typeArgs[1].Name}.g.cs", sourceCode.ToString());
        }
    }

    // 语法接收器:筛选Register<,>()格式的泛型方法调用
    private class ViewLocatorSyntaxReceiver : ISyntaxReceiver
    {
        public List<InvocationExpressionSyntax> MatchingInvocations { get; } = new();

        public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
        {
            if (syntaxNode is InvocationExpressionSyntax invocation
                && invocation.Expression is MemberAccessExpressionSyntax memberAccess
                && memberAccess.Name.Identifier.Text == "Register"
                && invocation.TypeArgumentList?.Arguments.Count == 2)
            {
                MatchingInvocations.Add(invocation);
            }
        }
    }
}

3. 配置生成器并验证

  1. 将生成器项目引用到主项目中,修改主项目的csproj文件,添加以下引用配置:
    <ProjectReference Include="..\ViewLocatorGenerator\ViewLocatorGenerator.csproj" 
                      OutputItemType="Analyzer" 
                      ReferenceOutputAssembly="false" />
    
  2. 编译主项目时,Source Generator会自动为每个Register<VM, V>()调用生成对应的扩展方法实现,编译后生成的代码会替换占位方法逻辑,最终运行效果与原始手动注册代码完全一致。

内容的提问来源于stack exchange,提问作者Etienne Charland

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 12:55:32