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

能否用.NET 6源代码生成器替代JINT实现JS与C#方法映射?

使用.NET 6源代码生成器实现JavaScript与C#方法映射

可行性说明

当然可以用.NET 6源代码生成器实现JS与C#方法的映射,而且相比JINT的运行时映射方案,这种编译期生成绑定代码的方式能彻底避开运行时反射开销,性能表现更出色。不过要注意:源代码生成器仅能处理编译期可见的固定映射关系,如果你的JS方法是运行时动态加载或变更的,那还是得依赖JINT的运行时绑定能力。

实现步骤示例

1. 定义标记特性

先创建一个自定义特性,用来标记需要和JS方法绑定的C#方法:

[AttributeUsage(AttributeTargets.Method, Inherited = false)]
public class JsMethodMappingAttribute : Attribute
{
    public string JsMethodName { get; }

    public JsMethodMappingAttribute(string jsMethodName)
    {
        JsMethodName = jsMethodName;
    }
}

2. 编写源代码生成器

实现ISourceGenerator接口,扫描带有上述特性的C#方法,自动生成JINT绑定代码:

[Generator]
public class JsMethodMappingGenerator : ISourceGenerator
{
    public void Initialize(GeneratorInitializationContext context)
    {
        context.RegisterForSyntaxNotifications(() => new MappedMethodReceiver());
    }

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

        var sourceBuilder = new StringBuilder(@"
using Jint;

public static class JsGeneratedBindings
{
    public static void RegisterAllBindings(Engine engine)
    {
");

        foreach (var method in receiver.TargetMethods)
        {
            // 提取特性中指定的JS方法名
            var mappingAttr = method.GetAttributes()
                .First(a => a.AttributeClass?.Name == nameof(JsMethodMappingAttribute));
            var jsMethodName = mappingAttr.ConstructorArguments[0].Value.ToString();

            // 拼接参数类型和返回值,生成绑定代码
            var paramTypes = string.Join(", ", method.Parameters.Select(p => p.Type.ToString()));
            var returnType = method.ReturnType.ToString();
            var methodFullName = $"{method.DeclaringType?.FullName}.{method.Name}";

            sourceBuilder.AppendLine($"        engine.SetValue(\"{jsMethodName}\", new System.Func<{paramTypes}, {returnType}>({methodFullName}));");
        }

        sourceBuilder.Append(@"
    }
}");

        context.AddSource("JsGeneratedBindings.g.cs", sourceBuilder.ToString());
    }

    // 语法接收器:收集所有标记了特性的C#方法
    private class MappedMethodReceiver : ISyntaxReceiver
    {
        public List<IMethodSymbol> TargetMethods { get; } = new();

        public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
        {
            if (syntaxNode is MethodDeclarationSyntax methodDecl && methodDecl.AttributeLists.Any())
            {
                var semanticModel = context.Compilation.GetSemanticModel(methodDecl.SyntaxTree);
                if (semanticModel.GetDeclaredSymbol(methodDecl) is IMethodSymbol methodSymbol &&
                    methodSymbol.GetAttributes().Any(a => a.AttributeClass?.Name == nameof(JsMethodMappingAttribute)))
                {
                    TargetMethods.Add(methodSymbol);
                }
            }
        }
    }
}

3. 实际使用

在你的业务代码中标记需要映射的C#方法:

public static class AppBusinessMethods
{
    [JsMethodMapping("calculateSum")]
    public static int Sum(int a, int b) => a + b;

    [JsMethodMapping("getUserGreeting")]
    public static string GreetUser(string username) => $"Hi, {username}!";
}

初始化JINT引擎时,直接调用生成的绑定方法:

var jintEngine = new Engine();
JsGeneratedBindings.RegisterAllBindings(jintEngine);

// 调用JS方法,实际会执行对应的C#逻辑
var sumResult = jintEngine.Execute("calculateSum(10, 20)").GetCompletionValue().ToObject<int>(); // 结果为30
var greeting = jintEngine.Execute("getUserGreeting('Alice')").GetCompletionValue().ToString(); // 结果为"Hi, Alice!"

关于源代码生成器的理解校验

如果你的认知是“源代码生成器是在编译阶段生成额外的C#代码,替代原本需要运行时通过反射/动态逻辑实现的绑定”,那这个理解完全正确。补充两个核心差异点帮你明确边界:

  • 运行时 vs 编译期:JINT的映射是动态的,可随时在运行时添加/修改;源代码生成器的绑定是编译时固定的,无法处理运行时新增的方法或JS逻辑。
  • 性能 vs 灵活性:生成器生成的代码是直接编译好的,没有反射开销,适合映射关系固定的场景;JINT的运行时映射更灵活,但存在一定的性能损耗。

内容的提问来源于stack exchange,提问作者Praneet Nadkar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 19:15:38