能否用.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
相关产品推荐
相关产品推荐

