|
2 | 2 | title: "常见问题"
|
3 | 3 | slug: "/常见问题"
|
4 | 4 | ---
|
| 5 | + |
| 6 | +<br/> |
| 7 | + |
| 8 | +### 我想使用轻量级动态编译,听说 Natasha 预热要好几十 M 的内存,而且打包的大小也增加了,我感觉太重了。 |
| 9 | + |
| 10 | +答: Natasha 的预热是给萌新准备的,本意是忽略所有的准备工作直接进行动态编译。但是无论是 emit 还是 表达式树,都要反射相关的元数据给动态编译使用,如果你有 emit 和 表达式树 相关编程经验,就能知道你所编写的动态功能都依赖什么元数据了,而 Natasha 可以不预热并支持自定义添加元数据,参见以下示例, 该实例输入为 value, 输出为 Math.Floor(value/0.3): |
| 11 | + |
| 12 | +#### emit 版本 |
| 13 | +```cs |
| 14 | + DynamicMethod dynamicMethod = new DynamicMethod("FloorDivMethod", typeof(double), new Type[] { typeof(double) }, typeof(Program).Module); |
| 15 | + |
| 16 | + ILGenerator ilGenerator = dynamicMethod.GetILGenerator(); |
| 17 | + |
| 18 | + ilGenerator.Emit(OpCodes.Ldarg_0); |
| 19 | + ilGenerator.Emit(OpCodes.Ldc_R8, 0.3); |
| 20 | + ilGenerator.Emit(OpCodes.Div); |
| 21 | + ilGenerator.Emit(OpCodes.Call, typeof(Math).GetMethod("Floor", new Type[] { typeof(double) })); |
| 22 | + ilGenerator.Emit(OpCodes.Ret); |
| 23 | + |
| 24 | + Func<double, double> floorDivMethod = (Func<double, double>)dynamicMethod.CreateDelegate(typeof(Func<double, double>)); |
| 25 | +``` |
| 26 | + |
| 27 | +#### 表达式树版本 |
| 28 | +```cs |
| 29 | + ParameterExpression valueParameter = Expression.Parameter(typeof(double), "value"); |
| 30 | + |
| 31 | + Expression divisionExpression = Expression.Divide(valueParameter, Expression.Constant(0.3)); |
| 32 | + Expression floorExpression = Expression.Call(typeof(Math), "Floor", null, divisionExpression); |
| 33 | + |
| 34 | + Expression<Func<double, double>> expression = Expression.Lambda<Func<double, double>>(floorExpression, valueParameter); |
| 35 | + |
| 36 | + Func<double, double> floorDivMethod = expression.Compile(); |
| 37 | +``` |
| 38 | + |
| 39 | +#### Natasha 版本 |
| 40 | +```cs |
| 41 | +AssemblyCSharpBuilder builder = new(); |
| 42 | +var func = builder |
| 43 | + .UseRandomLoadContext() |
| 44 | + .UseSimpleMode() |
| 45 | + .ConfigLoadContext(ctx => ctx |
| 46 | + .AddReferenceAndUsingCode(typeof(Math)) |
| 47 | + .AddReferenceAndUsingCode(typeof(double))) |
| 48 | + .Add("public static class A{ public static double Invoke(double value){ return Math.Floor(value/0.3); }}") |
| 49 | + .GetAssembly() |
| 50 | + .GetDelegateFromShortName<Func<double, double>>("A", "Invoke"); |
| 51 | +``` |
| 52 | + |
| 53 | +#### Natasha 方法模板封装版 |
| 54 | +> 该扩展库 `DotNetCore.Natasha.CSharp.Extension.MethodCreator` 在原 Natasha 基础上封装,并在 Natasha v9.0 版本后发布。 |
| 55 | +```cs |
| 56 | +var simpleFunc = "return Math.Floor(arg1/0.3);" |
| 57 | + .WithSimpleBuilder() |
| 58 | + .WithMetadata(typeof(Math)) |
| 59 | + .ToFunc<double, double>(); |
| 60 | +``` |
| 61 | + |
| 62 | +以上两种 Natasha 的动态编译都不会引起几十M 的内存涨幅,属于按需构建。 |
| 63 | +同时由此可以看出,无论哪种动态构建,都无法挣脱 `typeof(Math)` 的束缚,Natasha 同样支持轻量级的构建,甚至更加简洁。 |
| 64 | + |
| 65 | +### 我可以使用 Natasha 作为规则引擎吗? |
| 66 | + |
| 67 | +答:Natasha 的动态构建能力非常强大,它是可以作为编写其他库的基础,这也是我当初开发此库的本意,你可以用 Natasha 封装属于你的规则引擎。 |
| 68 | + |
| 69 | +### 我不想每次都编译,能否缓存脚本的结果? |
| 70 | + |
| 71 | +答:不可以,缓存结果需要您来完成,Natasha 走的是轻量化路线,`AssemblyCSharpBuilder` 为最小最基础的编译单元,如果你想缓存,可以自己实现一个 ConcurrentDictionary<string,Delegate/Action/Func> 来缓存结果。Natasha 不负责编译职责之外的事情。 |
| 72 | + |
| 73 | +### 我只想使用 using 全集,但不想添加那么多的引用。 |
| 74 | + |
| 75 | +答:Natasha 在 v9.0 版本后支持只预热 using 而不添加引用,新增了初始化方法: |
| 76 | +```cs |
| 77 | +NatashaManagement |
| 78 | + .GetInitializer() |
| 79 | + .WithMemoryUsing() |
| 80 | + //.WithRefUsing() |
| 81 | + //.WithMemoryReference() |
| 82 | + //.WithRefReference(); |
| 83 | + //.WithExcludeReferences((asm, @namespace) => |
| 84 | + //!string.IsNullOrWhiteSpace(@namespace) && |
| 85 | + //@namespace.StartsWith("Microsoft.VisualBasic")) |
| 86 | + .Preheating<NatashaDomainCreator>(); |
| 87 | +``` |
| 88 | +### 能否复用之前创建的编译单元? |
| 89 | + |
| 90 | +答:Natasha v9.0 之前不推荐复用,v9.0 后推出了 Api : `Reset`. 该方法有非常详细的注释引导和提示您需要注意的复用事项。 |
| 91 | + |
| 92 | +### 如果不懂元数据,也不想理 using 管理,能否写出一手好的可靠的动态功能逻辑? |
| 93 | + |
| 94 | +答:很勉强,Natasha 全预热的情况下也不可避免复杂环境带来的各种版本不一致,using 滥用等情况。就拿简单的例子来说: |
| 95 | +```cs |
| 96 | +namspace MyNamespace{ |
| 97 | + |
| 98 | + public static class File{ |
| 99 | + |
| 100 | + } |
| 101 | +} |
| 102 | +``` |
| 103 | +以上代码在 VS 开启隐式 using 情况下,也无法处理 File 在 System.IO 和 MyNamespace 之间引用不明的问题。 |
| 104 | +而 Natasha 的 using 预热有时要比 VS 的 using 覆盖更加全面,因此出错的概率可能会更大。 |
| 105 | +这需要编码规范来约束开发者,比如避开官方定义的类名,扩展类名等,比如使用 System.IO.File 避开命名空间引用冲突的问题。 |
0 commit comments