常见问题
我想使用轻量级动态编译,听说 Natasha 预热要好几十 M 的内存,而且打包的大小也增加了,我感觉太重了。
答: Natasha 的预热是给萌新准备的,本意是忽略所有的准备工作直接进行动态编译。但是无论是 emit 还是 表达式树,都要反射相关的元数据给动态编译使用,如果你有 emit 和 表达式树 相关编程经验,就能知道你所编写的动态功能都依赖什么元数据了,而 Natasha 可以不预热并支持自定义添加元数据,参见以下示例, 该实例输入为 value, 输出为 Math.Floor(value/0.3):
emit 版本
DynamicMethod dynamicMethod = new DynamicMethod("FloorDivMethod", typeof(double), new Type[] { typeof(double) }, typeof(Program).Module);
ILGenerator ilGenerator = dynamicMethod.GetILGenerator();
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Ldc_R8, 0.3);
ilGenerator.Emit(OpCodes.Div);
ilGenerator.Emit(OpCodes.Call, typeof(Math).GetMethod("Floor", new Type[] { typeof(double) }));
ilGenerator.Emit(OpCodes.Ret);
Func<double, double> floorDivMethod = (Func<double, double>)dynamicMethod.CreateDelegate(typeof(Func<double, double>));
表达式树版本
ParameterExpression valueParameter = Expression.Parameter(typeof(double), "value");
Expression divisionExpression = Expression.Divide(valueParameter, Expression.Constant(0.3));
Expression floorExpression = Expression.Call(typeof(Math), "Floor", null, divisionExpression);
Expression<Func<double, double>> expression = Expression.Lambda<Func<double, double>>(floorExpression, valueParameter);
Func<double, double> floorDivMethod = expression.Compile();
Natasha 版本
AssemblyCSharpBuilder builder = new();
var func = builder
.UseRandomLoadContext()
.UseSimpleMode()
.ConfigLoadContext(ctx => ctx
.AddReferenceAndUsingCode(typeof(Math))
.AddReferenceAndUsingCode(typeof(double)))
.Add("public static class A{ public static double Invoke(double value){ return Math.Floor(value/0.3); }}")
.GetAssembly()
.GetDelegateFromShortName<Func<double, double>>("A", "Invoke");
Natasha 方法模板封装版
该扩展库
DotNetCore.Natasha.CSharp.Extension.MethodCreator
在原 Natasha 基础上封装,并在 Natasha v9.0 版本后发布。
var simpleFunc = "return Math.Floor(arg1/0.3);"
.WithSimpleBuilder()
.WithMetadata(typeof(Math))
.ToFunc<double, double>();
以上两种 Natasha 的动态编译都不会引起几十M 的内存涨幅,属于按需构建。
同时由此可以看出,无论哪种动态构建,都无法挣脱 typeof(Math)
的束缚,Natasha 同样支持轻量级的构建,甚至更加简洁。
我可以使用 Natasha 作为规则引擎吗?
答:Natasha 的动态构建能力非常强大,它是可以作为编写其他库的基础,这也是我当初开发此库的本意,你可以用 Natasha 封装属于你的规则引擎。
我不想每次都编译,能否缓存脚本的结果?
答:不可以,缓存结果需要您来完成,Natasha 走的是轻量化路线,AssemblyCSharpBuilder
为最小最基础的编译单元,如果你想缓存,可以自己实现一个 ConcurrentDictionary<string,Delegate/Action/Func>
来缓存结果。Natasha 不负责编译职责之外的事情。
我只想使用 using 全集,但不想添加那么多的引用。
答:Natasha 在 v9.0 版本后支持只预热 using 而不添加引用,新增了初始化方法:
NatashaManagement
.GetInitializer()
.WithMemoryUsing()
//.WithRefUsing()
//.WithMemoryReference()
//.WithRefReference();
//.WithExcludeReferences((asm, @namespace) =>
//!string.IsNullOrWhiteSpace(@namespace) &&
//@namespace.StartsWith("Microsoft.VisualBasic"))
.Preheating<NatashaDomainCreator>();
能否复用之前创建的编译单元?
答:Natasha v9.0 之前不推荐复用,v9.0 后推出了 Api : Reset
. 该方法有非常详细的注释引导和提示您需要注意的复用事项。