5

Background:

We have a project with client side(Javascript) and server side(C#). There is a calculation logic need to run in both sides, so it is written in both Javascript and C#. We have many unit tests for the C# version classes. Our goal is to share the unit tests for both C# and Javascript implementation.

Current situation:

We are able to run the Javascript code in an embeded JS engine (Microsoft ClearScript). The code looks like this:

public decimal Calulate(decimal x, decimal y) 
{
     string script = @"
            var calc = new Com.Example.FormCalculater();
            var result = calc.Calculate({0}, {1});";

     this.ScriptEngine.Evaluate(string.Format(script, x, y));

     var result = this.ScriptEngine.Evaluate("result");
     return Convert.ToDecimal(result);
}

However, writing such classes takes a lot of effort. We are looking for a way to create such classes dynamically at runtime.

For exmample, we have a C# class (also has it JS version in a JS fle):

public class Calculator {
    public decimal Add(decimal x, decimal y){ ... }
    public decimal Substract(decimal x, decimal y){ ... }
    public decimal Multiply(decimal x, decimal y){ ... }
    public decimal Divide(decimal x, decimal y){ ... }
}

We want to create a dynamic class having the same methods but calling the Script engine to call the related JS code.

Is it possible to do it?

Zach
  • 5,715
  • 12
  • 47
  • 62
  • CodeDom may what are you finding. https://msdn.microsoft.com/en-us/library/y2k85ax6(v=vs.110).aspx – HungDL Mar 30 '15 at 08:55

3 Answers3

5

Sounds pretty easy. You don't even need to manually emit any IL nowadays :)

The easiest way would be to ignore the "create it dynamically" part. You can simply use a T4 template to create the class automatically at compile-time. If your only consideration is unit tests, this is a pretty easy way to solve your problem.

Now, if you want to really create the type dynamically (at runtime), this gets a bit more complicated.

First, create an interface that contains all the required methods. The C# class will just implement this interface directly, while we'll generate the helper class to conform to this interface.

Next, we create the helper class:

var assemblyName = new AssemblyName("MyDynamicAssembly");
var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
var moduleBuilder = assemblyBuilder.DefineDynamicModule("Module");

var typeBuilder = moduleBuilder.DefineType("MyNewType", TypeAttributes.Public | TypeAttributes.Class | TypeAttributes, typeof(YourClassBase), new[] { typeof(IYourInterface) } );

The TypeBuilder allows us to define all those methods, so let's do that next.

// Get all the methods in the interface
foreach (var method in typeof(IYourInterface).GetMethods())
{
  var parameters = method.GetParameters().Select(i => i.ParameterType).ToArray();

  // We can only compile lambda expressions into a static method, so we'll have this helper. this is going to be YourClassBase.
  var helperMethod = typeBuilder.DefineMethod
        (
            "s:" + method.Name,
            MethodAttributes.Private | MethodAttributes.Static,
            method.ReturnType,
            new [] { method.DeclaringType }.Union(parameters).ToArray()
        );

  // The actual instance method
  var newMethod = 
    typeBuilder.DefineMethod
        (
            method.Name, 
            MethodAttributes.Public | MethodAttributes.Virtual, 
            method.ReturnType,
            parameters
        );

  // Compile the static helper method      
  Build(method).CompileToMethod(helperMethod);

  // We still need raw IL to call the helper method
  var ilGenerator = newMethod.GetILGenerator();

  // First argument is (YourClassBase)this, then we emit all the other arguments.
  ilGenerator.Emit(OpCodes.Ldarg_0);
  ilGenerator.Emit(OpCodes.Castclass, typeof(YourClassBase));
  for (var i = 0; i < parameters.Length; i++) ilGenerator.Emit(OpCodes.Ldarg, i + 1);

  ilGenerator.Emit(OpCodes.Call, helperMethod);
  ilGenerator.Emit(OpCodes.Ret);

  // "This method is an implementation of the given IYourInterface method."
  typeBuilder.DefineMethodOverride(newMethod, method);
}

To create the helper method body, I'm using these two helper methods:

LambdaExpression Build(MethodInfo methodInfo)
{
  // This + all the method parameters.
  var parameters = 
    new [] { Expression.Parameter(typeof(YourClassBase)) }
    .Union(methodInfo.GetParameters().Select(i => Expression.Parameter(i.ParameterType)))
    .ToArray();

  return
    Expression.Lambda
    (
      Expression.Call
      (
        ((Func<MethodInfo, YourClassBase, object[], object>)InvokeInternal).Method,
        Expression.Constant(methodInfo, typeof(MethodInfo)),
        parameters[0],
        Expression.NewArrayInit(typeof(object), parameters.Skip(1).Select(i => Expression.Convert(i, typeof(object))).ToArray())
      ),     
      parameters
    );
}

public static object InvokeInternal(MethodInfo method, YourClassBase @this, object[] arguments)
{
  var script = @"
    var calc = new Com.Example.FormCalculater();
    var result = calc.{0}({1});";

  script = string.Format(script, method.Name, string.Join(", ", arguments.Select(i => Convert.ToString(i))));

  @this.ScriptEngine.Evaluate(script);

  return (object)Convert.ChangeType(@this.ScriptEngine.Evaluate("result"), method.ReturnType);
}

If you want, you can make this a lot more specific (generate the expression tree to be a better match for the given method), but this saves us a lot of trouble and allows us to use C# for most of the hard stuff.

I'm assuming all your methods have a return value. If not, you'll have to adjust for that.

And finally:

var resultingType = typeBuilder.CreateType();

var instance = (IYourInterface)Activator.CreateInstance(resultingType);
var init = (YourClassBase)instance;
init.ScriptEngine = new ScriptEngine();

var result = instance.Add(12, 30);
Assert.AreEqual(42M, result);

Just for completeness, here's the IYourInterface and YourClassBase I've used:

public interface IYourInterface
{
  decimal Add(decimal x, decimal y);
}

public abstract class YourClassBase
{
  public ScriptEngine ScriptEngine { get; set; }
}

I do strongly suggest using text templates to generate the source code in compile-time, if you can, though. Dynamic code tends to be tricky to debug (and write, of course). On the other hand, if you just generate this stuff from a template, you'll see the whole generated helper class in code.

Luaan
  • 62,244
  • 7
  • 97
  • 116
1

CodeDom may what are you finding. https://msdn.microsoft.com/en-us/library/y2k85ax6(v=vs.110).aspx

Here is a good example: http://www.codeproject.com/Articles/26312/Dynamic-Code-Integration-with-CodeDom

HungDL
  • 493
  • 2
  • 11
0

You might be able to use C#'s dynamic to share unit test code. Suppose you have a C# class:

public class Calculator {
    public decimal Add(decimal x, decimal y) { return x + y; }
}

Let's say you've also created a JavaScript object that implements an identical interface:

scriptEngine.Execute(@"
    calculator = {
        Add: function (x, y) { return x + y; }
    };
");

You can create one test method for both:

public static void TestAdd(dynamic calculator) {
    Assert.AreEqual(3, calculator.Add(1, 2));
}

And here's how you'd test both implementations:

TestAdd(new Calculator());
TestAdd(scriptEngine.Script.calculator);

What's nice about this is that you aren't parsing and compiling new script code for each test call.

BitCortex
  • 3,328
  • 1
  • 15
  • 19