Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a C# class (according to an existing class) dynamically at runtime

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?

like image 655
Zach Avatar asked Mar 30 '15 08:03

Zach


2 Answers

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.

like image 96
Luaan Avatar answered Nov 15 '22 00:11

Luaan


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

like image 23
HungDL Avatar answered Nov 14 '22 23:11

HungDL