单声道编译器即服务(MCS)

问题描述:

我想从我的常规.NET 3.5应用程序中使用Mono的编译器作为服务。单声道编译器即服务(MCS)

我已经下载了最新的位(2.6.7),在Visual Studio中创建了一个简单的控制台应用程序,并引用了Mono.CSharp dll。

然后,在我的控制台应用程序(直出样品在线):

Evaluator.Run("using System; using System.Linq;"); 
    bool ress; 
    object res; 
    Evaluator.Evaluate(
     "from x in System.IO.Directory.GetFiles (\"C:\\\") select x;", 
     out res, out ress); 

    foreach (var v in (IEnumerable)res) 
    { 
     Console.Write(v); 
     Console.Write(' '); 
    } 

这将引发在Evaluator.Run异常(第一行):

Illegal enum value: 2049. 
Parameter name: access 

这是因为我相信dll是使用Mono.exe编译的,而不是csc.exe。

我试过在demo-repl.zip文件中直接从http://tirania.org/blog/archive/2010/Apr-27.html下载Mono.CSharp dll ...并且不会引发异常......但是调用Evaluator.Evaluate之后的out参数(res)是空的...所以我不知道发生了什么问题。没有例外抛出...

所以,我想弄清楚为什么我从demo-repl.zip下载的DLL返回null。

编辑:我想通了它为什么返回null。这似乎是由于某种原因编译器没有拿起System.Linq命名空间......虽然我不能说出为什么......如果我只是评估“System.IO.Directory.GetFiles(\”C:\\ “)”,它工作正常。

更新:它看起来像是单引擎编译器拾取引用的系统程序集有问题。如果我直接在其CSHARP控制台工具的样本复制:

csharp> var list = new int [] {1,2,3}; 
csharp> var b = from x in list 
    > where x > 1 
    > select x; 
csharp> b; 

我得到异常:

{interactive}(1,25): error CS1935: An implementation of `Select' query expressio 
n pattern could not be found. Are you missing `System.Linq' using directive or ` 
System.Core.dll' assembly reference? 

此外,为了使MCS实际上是一个可行的解决方案,我需要修改编译器,以便它发出一个动态程序集,而不是每次评估调用都发出一个程序集(否则会出现一个主要的内存泄漏,我之前已经用CSharpCodeProvider的形式处理过)。有没有人知道这将是多么困难,或者有谁能够指出我在这个正确的方向?

谢谢。

+0

为什么不使用http://msdn.microsoft.com/en-us/library/microsoftoft.csharp.csharpcodeprovider(VS.80).aspx? – Andrey 2010-08-04 15:53:07

+1

CSharpCodeProvider每次编译都会发出并加载程序集(即使只使用内存中的选项)。我将执行数千次评估,因此会有数千个程序集加载到正在执行的AppDomain中(内存泄漏)。此外,CSharpCodeProvider内部使用csc.exe,这比Reflection.Emit处理器密集得多。在之前的项目中,我使用了CSharpCodeProvider,并在根据程序集数量回收的单独应用程序域中运行评估,但事实证明这是一个巨大的维护开销,并且非常容易出错,所以我想避免这种方法。 – Jeff 2010-08-04 16:03:40

+0

另外,我意识到,默认情况下,MCS在每次评估时也会执行一个程序集,但是由于它在内部依赖于Reflection.Emit,我希望能够更改该行为以发送到由AppDomain.DefineDynamicAssembly定义的单个程序集。 – Jeff 2010-08-04 16:05:53

好吧,我想我有一些答案。

要解决组装负荷的问题,我可以拨打电话里面Mono.CSharp.Driver.LoadAssembly Assembly.LoadWithPartialName,或做在我的应用

 AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve); 

     private static bool isResolving; 
     static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) 
     { 
      if (!isResolving) 
      { 
       isResolving = true; 
       var a = Assembly.LoadWithPartialName(args.Name); 
       isResolving = false; 
       return a; 
      } 
      return null; 
     } 

为了使单重用同一以下动态汇编每个评估/编译调用,所有我不得不改变以下(虽然可能有复杂性,我在这里失踪).....

Inside Mono.CSharp。计算器,我加了属性:

/// <summary> 
/// Gets or sets a value indicating whether to auto reset when evaluations are performed and create a new assembly. 
/// </summary> 
/// <value><c>true</c> if [auto reset]; otherwise, <c>false</c>.</value> 
public static bool AutoReset { get; set; } 

然后...确保复位初始化中至少调用一次:

static void Init() 
    { 
     Init (new string [0]); 
     Reset(); 
    } 

最后,在ParseString,根本不复位,除非自动复位真...

 static CSharpParser ParseString (ParseMode mode, string input, out bool partial_input) 
     { 
. 
. 
. 
      if (AutoReset) Reset(); 
+0

所以,我有这个相同的问题。是否有一个编译版本的Mono.CSharp.dll,其中包含上述更改?我想在应用程序中使用它,但我发现它非常不稳定。 – 2010-09-13 15:09:40

+0

你有什么问题?你有没有尝试过上面提到的改变?如果你真的需要它,我可以提供一个dll,但是你可能更好的做出这些更改并自己编译dll。 – Jeff 2010-09-13 16:56:18

+0

另外,在我的最后,在做出这些改变之后,我还没有真的遇到过其他问题......是吗? – Jeff 2010-09-13 18:01:53

根据您链接的Miguel的博客页面,您必须添加对System.Core的引用才能在.Net上使用LINQ。

csharp> using System.Linq; 
csharp> from x in "Foo" select x; 
+0

啊......我明白了,但添加引用是不够的,因为它不会将GAC程序集或框架程序集复制到bin目录。 – Jeff 2010-08-04 21:25:54