使用外部定义的类型的CSharpScript-无法从类型A转换为A

时间:2019-04-29 15:02:59

标签: c# .net-core .net-assembly csharpscript

问题:无法在CSharpScript中使用外部定义的类型,因为由于某些程序集不匹配,它无法将对象类型从其自身转换为自身。

我有2个项目。

常见

using System;

namespace Common
{
    public class Arguments
    {
        public string Text;
    }

    public class Output
    {
        public bool Success;
    }
}

CSharpScriptingExperiment

using System;
using System.Collections.Generic;
using Common;
using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Scripting;

public class Parameters
{
    public string text;

    public Arguments arguments;
}

namespace CSharpScriptingExperiment
{
    class Program
    {
        static void Main(string[] args)
        {   
            ScriptOptions options = ScriptOptions.Default.WithImports(new List<string>() { "Common" });

            options = options.AddReferences(typeof(Arguments).Assembly);

            // Script will compare the text inside arguments object to the text passed in via function parameters
            var script = CSharpScript.Create(@"
                public class TestClass
                {
                    public Output DoSomething(string text, Arguments args)
                    {
                        return new Output() { Success = args.Text == text };
                    }
                }", options: options, globalsType: typeof(Parameters));

            var nextStep = script.ContinueWith<object>("return new TestClass().DoSomething(text, arguments);");

            // Setup the global paramters object
            Parameters parameters = new Parameters();
            parameters.text = "Hello";
            parameters.arguments = new Arguments()
            {
                Text = "Hello"
            };

            // Run script
            Output output = (Output)nextStep.RunAsync(globals: parameters).Result.ReturnValue;

            Console.WriteLine(output.Success);
            Console.ReadLine();
        }
    }
}

运行 CSharpScriptingExperiment 时,出现此错误:

"(1,42): error CS1503: Argument 2: cannot convert from 'Common.Arguments [/Users/username/Projects/CSharpScriptingExperiment/CSharpScriptingExperiment/bin/Debug/netcoreapp2.2/Common.dll]' to 'Common.Arguments [Common, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]'"

在这一行:

Output output = (Output)nextStep.RunAsync(globals: parameters).Result.ReturnValue;

通用是一个.NET Standard 2.0项目。

CSharpScriptingExperiment 是一个.NET Core 2.2项目。

有什么想法吗?我见过其他人遇到类似的问题,但没有找到解决方案。

1 个答案:

答案 0 :(得分:0)

通过一些变通方法解决了该问题。

关于CSharpScript,有2个不同的作用域。一个是函数,类等内部代码的常规C#范围。第二个是CSharpScript的独特功能,即静态范围。它允许变量和代码运行而无需使用类或函数-类似于REPL。

问题是,当将外部类型对象加载到静态范围中时,该类型又应该传递给接受该类型参数的函数,则静态范围和常规范围的对象表示形式为不兼容。导致问题的原因是中间静态作用域。

它是这样的:

Executing Assembly -> Script Static Scope -> Script Normal Scope

这会导致上述问题。

这样做时:

Executing Assembly -> Script Normal Scope

Script Normal Scope -> Executing Assembly

一切正常。

因此,我可以将外部类型对象从函数返回到正在执行的程序集,但不能先通过静态作用域将外部类型对象传递给函数。

解决方法是在函数中接受object,然后将对象强制转换为函数内部的外部类型。

using System;
using System.Collections.Generic;
using Common;
using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Scripting;

public class Parameters
{
    public string text;

    public Arguments arguments;
}

namespace CSharpScriptingExperiment
{
    class Program
    {
        static void Main(string[] args)
        {   
            ScriptOptions options = ScriptOptions.Default.WithImports(new List<string>() { "Common" });

            options = options.AddReferences(typeof(Arguments).Assembly);

            // Script will compare the text inside arguments object to the text passed in via function parameters
            var script = CSharpScript.Create(@"
                public class TestClass
                {
                    public Output DoSomething(string text, object arguments)
                    {
                        Arguments args = (Arguments)arguments;
                        return new Output() { Success = args.Text == text };
                    }
                }", options: options, globalsType: typeof(Parameters));

            var nextStep = script.ContinueWith<object>("return new TestClass().DoSomething(text, arguments);");

            // Setup the global paramters object
            Parameters parameters = new Parameters();
            parameters.text = "Hello";
            parameters.arguments = new Arguments()
            {
                Text = "Hello"
            };

            // Run script
            Output output = (Output)nextStep.RunAsync(globals: parameters).Result.ReturnValue;

            Console.WriteLine(output.Success);
            Console.ReadLine();
        }
    }
}

因此,核心要点是,只要对象不通过静态方式的范围,事情就会按预期进行。如果对象需要通过静态作用域,则将其作为对象处理,并在目标函数内部强制转换为所需的任何内容-脚本引擎似乎在强制转换自身时遇到问题,并且类型从根本上发生冲突。 / p>

这纯粹是基于黑匣子测试和调试-我希望罗斯林(Roslyn)团队对此有所帮助,或者是一个进行内部检查以检查我的直觉和发现是否正确的人。

希望它可以帮助遇到此问题的其他人!