Setting the value of a variable in the Mono.CSharp Evaluator

When working with the Mono.CSharp Evaluator, you can define some variables and then use them later on to evaluate other expression. Here is a simple example:

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;");
            evaluator.Run("i=5+1;");
            string vars = evaluator.GetVars();
            Console.WriteLine(vars);
        }
    }
}

It defines a variable i and then sets it to some value. On the console, you will see:

int i=6

Now, sometimes you need to set the variable to a value which you cannot set so easily i.e. set it to a value which you cannot express with a string like 5+1.

There is unfortunately no method putVar(…) in the Evaluator class. Also the repository for variables which is internally maintained by the Evaluator is not exposed to you. But at least it does exist. So this means we’ll need some reflection to get to it.

First let’s have a look at the internal data maintained by the Evaluator:

Quick watch Evaluator

So the Evaluator has a non-public member called fields which is a collection of field definition/values. So what we need to do to set a value is either directly had an entry in there or modify an existing entry. Adding a new entry is kind of difficult because you need to define a Mono.CSharp.FieldSpec as item1 and this is no piece of cake. So our best shot is to use the Run method to create a variable. Once it is already in the fields member, we just need to update its item2 to the value we want to set.

First let’s get the fields member. We know it’s a non-public instance member so we can find it like this:

FieldInfo fieldInfo = typeof(Evaluator).GetField("fields", BindingFlags.NonPublic | BindingFlags.Instance);
var fields = fieldInfo.GetValue(evaluator) as Dictionary<string, Tuple<FieldSpec, FieldInfo>>;

Now we have the dictionary, we need to find the entry for our variable:

Tuple<FieldSpec, FieldInfo> tuple = fields["i"];

And then we can set item2 to the appropriate value:

tuple.Item2.SetValue(evaluator, 5 + 1);

That’s it. Putting it all together, you get the following code:

using System;
using System.Collections.Generic;
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;");
            FieldInfo fieldInfo = typeof(Evaluator).GetField("fields", BindingFlags.NonPublic | BindingFlags.Instance);
            if (fieldInfo != null)
            {
                var fields = fieldInfo.GetValue(evaluator) as Dictionary<string, Tuple<FieldSpec, FieldInfo>>;
                if (fields != null)
                {
                    Tuple<FieldSpec, FieldInfo> tuple = fields["i"];
                    if (tuple != null)
                    {
                        tuple.Item2.SetValue(evaluator, 5 + 1);
                    }
                }
            }
            string vars = evaluator.GetVars();
            Console.WriteLine(vars);
        }
    }
}

And as expected it writes int i=6 to the console. Of course in this case it makes no sense to do it this way as you could do it as shown at the beginning of the port with one line of code. But with this approach you can set values which are very complex and cannot be fully converted to a string.

If you do not like this approach, there is a question on StackOverflow which provides a few different ways to handle this issue.

Here is a short explanation as to why I did not use any of these 3 solution approaches:

  1. Use static or ThreadStatic variables: They have done a nice job in moving all methods of the Evaluator class from static methods to instance methods so that you can use different Evaluators in parallel and using static variables just takes us back to the time when only one Evaluator could be used.
  2. Return a delegate from your string code: The code I am interpreting is written by a used in a web UI. I am loading the value I want to set to a special variable using some class I do not want to give access to. The user should have access to the created object but not to the factory. Using this method means I need to add access to the assembly containing the factory, not only to the assembly containing the model.
  3. Go the full blown route: This basically has the same issue as the second solution.

So if you are not afraid to use reflection, need to be able to have multiple Evaluator instances in parallel and do not want to have the interpreter have access to the factory used to generate objects, the solution approach described in my post is the only one I can thing of.

One thought on “Setting the value of a variable in the Mono.CSharp Evaluator

  1. I want to modify a property of a .NET control in an ASP.NET web form using Mono, something like this:

    using Mono.Csharp;

    public void Test()
    {
    var assembly = Assembly.GetAssembly(typeof(_Default));
    CompilerSettings settings = new CompilerSettings();
    ReportPrinter printer = new ConsoleReportPrinter();
    CompilerContext context = new CompilerContext(settings, printer);
    Evaluator evaluator = new Evaluator(context);
    evaluator.ReferenceAssembly(assembly);
    evaluator.Run(“Button1.BackColor = Color.Yellow;”);
    }
    The evaluator.Run() statement does not alter the color of Button1, although the control and its properties are visible at this point. What is the correct way to do this? (In practice, the string inside the Run() statement would be selected from a table).

Leave a Reply

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