首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >脚本评估器LoadCode:如何编译和引用第二个脚本(可重用库)

脚本评估器LoadCode:如何编译和引用第二个脚本(可重用库)
EN

Stack Overflow用户
提问于 2013-12-17 21:24:19
回答 2查看 4.5K关注 0票数 2

简而言之,问题是:如何引用包含可重用脚本代码的第二个脚本,在不重新启动主机应用程序的情况下卸载和重新加载脚本?

我试图使用CS-脚本“编译器即服务”(CSScript.Evaluator) ()编译一个脚本类,同时引用刚刚从第二个“库”脚本编译的程序集。其目的是库脚本应该包含可用于不同脚本的代码。

下面是一个示例代码,它说明了这个想法,但也导致了运行时的CompilerException。

代码语言:javascript
复制
using CSScriptLibrary;
using NUnit.Framework;

[TestFixture]
public class ScriptReferencingTests
{
    private const string LibraryScriptCode = @"
public class Helper
{
    public static int AddOne(int x)
    {
        return x + 1;
    }
}
";

    private const string ScriptCode = @"
using System;
public class Script
{
    public int SumAndAddOne(int a, int b)
    {
        return Helper.AddOne(a+b);
    }
}
";

    [Test]
    public void CSScriptEvaluator_CanReferenceCompiledAssembly()
    {
        var libraryEvaluator = CSScript.Evaluator.CompileCode(LibraryScriptCode);
        var libraryAssembly = libraryEvaluator.GetCompiledAssembly();
        var evaluatorWithReference = CSScript.Evaluator.ReferenceAssembly(libraryAssembly);
        dynamic scriptInstance = evaluatorWithReference.LoadCode(ScriptCode);

        var result = scriptInstance.SumAndAddOne(1, 2);

        Assert.That(result, Is.EqualTo(4));
    }
}

要运行代码,您需要NuGet包、、NUnit和cs-script。

这一行在运行时导致一个CompilerException:

代码语言:javascript
复制
dynamic scriptInstance = evaluatorWithReference.LoadCode(ScriptCode);

{interactive}(7,23): error CS0584: Internal compiler error: The invoked member is not supported in a dynamic assembly.

{interactive}(7,9): error CS0029: Cannot implicitly convert type '<fake$type>' to 'int'

同样,使用CSScript.Evaluator.LoadCode而不是CSScript.LoadCode的原因是,在任何一个脚本更改时,都可以在不重新启动主机应用程序的情况下随时重新加载脚本。(CSScript.LoadCode已经支持根据scripts.html包括其他脚本)

以下是CS-脚本评估器的文档:http://www.csscript.net/help/evaluator.html

谷歌对此结果的缺乏令人沮丧,但我希望我错过了一些简单的东西。任何帮助都将不胜感激。

(此问题应在不存在的cs标记下存档。)

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2013-12-18 16:04:43

CompilerException的快速解决方案似乎不是使用计算器编译程序集,而是像这样的CSScript.LoadCode

代码语言:javascript
复制
var compiledAssemblyName = CSScript.CompileCode(LibraryScriptCode);
var evaluatorWithReference = CSScript.Evaluator.ReferenceAssembly(compiledAssemblyName);
dynamic scriptInstance = evaluatorWithReference.LoadCode(ScriptCode);

但是,正如前面的答案所述,这限制了CodeDOM模型提供的依赖控制的可能性(如css_include)。此外,没有看到对LibraryScriptCode的任何更改,这再次限制了评估器方法的有用性。

我选择的解决方案是AsmHelper.CreateObjectAsmHelper.AlignToInterface<T>方法。这允许您在脚本中使用常规的css_include,同时允许您在任何时候通过配置AsmHelper并重新开始重新加载脚本。我的解决方案如下所示:

代码语言:javascript
复制
AsmHelper asmHelper = new AsmHelper(CSScript.Compile(filePath), null, false);
object obj = asmHelper.CreateObject("*");
IMyInterface instance = asmHelper.TryAlignToInterface<IMyInterface>(obj);
// Any other interfaces you want to instantiate...
...
if (instance != null)
    instance.MyScriptMethod();

一旦检测到更改(我使用FileSystemWatcher),您只需调用asmHelper.Dispose并再次运行上述代码。

此方法要求用Serializable属性标记脚本类,或者直接从MarshalByRefObject继承。

注意,您的脚本类不需要继承任何接口。AlignToInterface既可以使用,也可以不使用它。您可以在这里使用dynamic,但我更希望有一个强类型的接口,以避免出现错误。

我无法让try-methods中的内置程序正常工作,所以当不知道接口是否实现时,为了减少杂乱,我使用了这个扩展方法:

代码语言:javascript
复制
public static class InterfaceExtensions
{
    public static T TryAlignToInterface<T>(this AsmHelper helper, object obj) where T : class
    {
        try
        {
            return helper.AlignToInterface<T>(obj);
        }
        catch
        {
            return null;
        }
    }
}

其中大部分是在托管指南.html中解释的,前面的文章中提到了一些有用的示例。

我觉得我可能错过了一些关于脚本更改检测的内容,但是这种方法确实有效。

票数 0
EN

Stack Overflow用户

发布于 2013-12-18 00:20:13

这里有点混乱。Evaluator不是实现可重加载脚本行为的唯一方法。CSScript.LoadCode也允许重新加载。

我确实建议将CSScript.Evaluator.LoadCode作为主机模型的第一个候选,因为它提供了更少的开销,并且可以说更方便地重新加载模型。然而,它也伴随着成本。您对重新加载和依赖项包含(程序集、脚本)几乎没有控制。内存泄漏不是100%可以避免的。它还使得脚本调试完全不可能(Mono )。

在您的例子中,我建议您改用更传统的托管模式: CodeDOM。

看一下"[cs-script]\Samples\Hosting\CodeDOM\Modifying script without restart"样本。

"[cs-script]\Samples\Hosting\CodeDOM\InterfaceAlignment"还将向您介绍如何在重新加载时使用接口。

多年来,CodeDOM一直是默认的CS脚本托管模式,它实际上非常健壮、直观和易于管理。惟一的真正的缺点是,您传递给(或从)脚本的所有对象都需要是可序列化的或从MarshalByRef继承的。这是脚本在“自动”单独域中执行的副作用。因此,我们必须处理远程处理的所有“乐趣”。顺便说一句,这是我实现基于Mono的评估器的唯一原因。

CodeDOM模型还将自动管理依赖项,并在需要时重新编译它们。但看起来你还是知道这件事的。

CodeDOM还允许您精确地定义检查更改的依赖项的机制:

代码语言:javascript
复制
//the default algorithm "recompile if script or dependency is changed"
CSScript.IsOutOfDateAlgorithm = CSScript.CachProbing.Advanced; 

代码语言:javascript
复制
//custom algorithm "never recompile script" 
CSScript.IsOutOfDateAlgorithm = (s, a) => false;
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/20644984

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档