Issue with Mono.CSharp and NRefactory

In a project I am working on, we’re using Mono.Scharp to interpret C# code at runtime (the code is entered by the user of the software). This can be done either with Mono.CSharp or using CSharpCodeProvider (part of Microsoft.CSharp). The nice thing about Mono.CSharp is that it’s implemented in a way that it’s easy to interpret code without needing to write a whole program around it. The error messages can thus directly be shown to the user since you do not need to add anything to their code before compiling or interpreting it.

If additionally to the dynamic code evaluation you also need to throw in some code completion, you can also use Mono.CSharp for it. But you’ll quickly notice that the Evaluator tends to hang forever with a high CPU usage on some expressions (especially when using the LINQ SQL syntax or when working with anonymous types). That’s the reason why I’ve decided to add NRefactory to the mix.

NRefactory works great. It provide a much better code completion than the Evaluator of Mono.CSharp does and it never chocks on code (or at least I haven’t observed it anymore after we switched). One problem is that NRefactory does rely on Mono.CSharp and that instead of having it as a dependency, it delivers it within its assembly. So this means that if you have both in your project, Visual Studio will keep complaining that it doesn’t Mono.CSharp.Evaluator is provided by both assemblies and it doesn’t know which one to use.

My first thought was to ditch Mono.CSharp as I get it from NRefactory anyway. But then I noticed that the Evaluator in NRefactory doesn’t seem to work at all with variables. Let’s have a look at a very simple program:

using System;
using System.Reflection;
using Mono.CSharp;

namespace Playground
{
    class Program
    {
        static void Main(string[] args)
        {
            var evaluator = new Evaluator(new CompilerContext(new CompilerSettings { WarningLevel = 0, ShowFullPaths = true }, new ConsoleReportPrinter()));
            evaluator.Run("int i=0;");
            string vars = evaluator.GetVars();
            Console.WriteLine(vars);
        }
    }
}

It just instanciates an evaluator and defines a variable i. Using Mono.CSharp.dll, you’ll see the following on the console:

int i = 0

If you now remove the reference to Mono.CShap.dll and add NRefactory in there (e.g. using the NuGet package manager), your code will still compile but you will get the following Exception at runtime:

(1,9): error CS0589: Internal compiler error during parsingSystem.NullReferenceException: Object reference not set to an instance of an object.
at Mono.CSharp.CSharpParser.case_794()
at Mono.CSharp.CSharpParser.yyparse(yyInput yyLex)
at Mono.CSharp.CSharpParser.parse()

Never found a way to get it working… So I looked for a solution using NRecfatory for code completion but Mono.CSharp for interpreting the code.

Basically, all you need to do is let the compiler know that when you write Mono.CSharp.xxx you mean the one in the Mono.CSharp.dll assembly and not the one in the NRefactory assembly. Once you’ve got both as references, you’ll get the following errors in your project:

Description File Line Column Project
Error 1 The type ‘Mono.CSharp.Evaluator’ exists in both ‘C:\Software\Visual Studio 2012\Projects\Playground\packages\ICSharpCode.NRefactory.5.3.0\lib\Net40\ICSharpCode.NRefactory.CSharp.dll’ and ‘C:\Software\Visual Studio 2012\Projects\Playground\packages\Mono.CSharp.3.0.6\Lib\net40\Mono.CSharp.dll’ C:\Software\Visual Studio 2012\Projects\Playground\Playground\Program.cs 11 33 Playground
Error 2 The type ‘Mono.CSharp.CompilerContext’ exists in both ‘C:\Software\Visual Studio 2012\Projects\Playground\packages\ICSharpCode.NRefactory.5.3.0\lib\Net40\ICSharpCode.NRefactory.CSharp.dll’ and ‘C:\Software\Visual Studio 2012\Projects\Playground\packages\Mono.CSharp.3.0.6\Lib\net40\Mono.CSharp.dll’ C:\Software\Visual Studio 2012\Projects\Playground\Playground\Program.cs 11 47 Playground
Error 3 The type ‘Mono.CSharp.CompilerSettings’ exists in both ‘C:\Software\Visual Studio 2012\Projects\Playground\packages\ICSharpCode.NRefactory.5.3.0\lib\Net40\ICSharpCode.NRefactory.CSharp.dll’ and ‘C:\Software\Visual Studio 2012\Projects\Playground\packages\Mono.CSharp.3.0.6\Lib\net40\Mono.CSharp.dll’ C:\Software\Visual Studio 2012\Projects\Playground\Playground\Program.cs 11 67 Playground
Error 4 The type ‘Mono.CSharp.ConsoleReportPrinter’ exists in both ‘C:\Software\Visual Studio 2012\Projects\Playground\packages\ICSharpCode.NRefactory.5.3.0\lib\Net40\ICSharpCode.NRefactory.CSharp.dll’ and ‘C:\Software\Visual Studio 2012\Projects\Playground\packages\Mono.CSharp.3.0.6\Lib\net40\Mono.CSharp.dll’ C:\Software\Visual Studio 2012\Projects\Playground\Playground\Program.cs 11 132 Playground

Now all you need to do is to select the reference to Mono.CSharp.dll in the Solution Explorer and modify the property Aliases to include not only global but an alias of your own e.g. MonoCSharp:

Mono.CSharp Alias

After that you can just reference this alias in your code:

extern alias MonoCSharp;

You can then use this as a prefix before using a type from Mono.CSharp:

var evaluator = new MonoCSharp::Mono.CSharp.Evaluator(
    new MonoCSharp::Mono.CSharp.CompilerContext(
        new MonoCSharp::Mono.CSharp.CompilerSettings { 
            WarningLevel = 0, 
            ShowFullPaths = true 
        }, new MonoCSharp::Mono.CSharp.ConsoleReportPrinter()
    )
);

Like this, you can reference both Mono.CSharp and NRefactory and still be able to use the better Evaluator from Mono.CSharp.dll.

2 thoughts on “Issue with Mono.CSharp and NRefactory

Leave a Reply

Your email address will not be published. Required fields are marked *