Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/332.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 如何在运行时动态创建C#类(根据现有类)_Javascript_C#_Emit_Clearscript - Fatal编程技术网

Javascript 如何在运行时动态创建C#类(根据现有类)

Javascript 如何在运行时动态创建C#类(根据现有类),javascript,c#,emit,clearscript,Javascript,C#,Emit,Clearscript,背景: 我们有一个客户端(Javascript)和服务器端(C#)的项目。两边都需要运行一个计算逻辑,因此它是用Javascript和C#编写的。 我们有许多针对C版本类的单元测试。我们的目标是共享C#和Javascript实现的单元测试 当前情况: 我们能够在嵌入式JS引擎(Microsoft ClearScript)中运行Javascript代码。代码如下所示: public decimal Calulate(decimal x, decimal y) { string scri

背景:

我们有一个客户端(Javascript)和服务器端(C#)的项目。两边都需要运行一个计算逻辑,因此它是用Javascript和C#编写的。 我们有许多针对C版本类的单元测试。我们的目标是共享C#和Javascript实现的单元测试

当前情况:

我们能够在嵌入式JS引擎(Microsoft ClearScript)中运行Javascript代码。代码如下所示:

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);
}
然而,编写这样的类需要很多努力。我们正在寻找一种在运行时动态创建此类类的方法

例如,我们有一个C#类(在JS fle中也有JS版本):

我们希望创建一个具有相同方法的动态类,但是调用脚本引擎来调用相关的JS代码


有可能做到吗?

听起来很简单。现在您甚至不需要手动发出任何IL:)

最简单的方法是忽略“动态创建”部分。您可以简单地使用T4模板在编译时自动创建类。如果您只考虑单元测试,那么这是解决问题的一种非常简单的方法

现在,如果您真的想动态地(在运行时)创建类型,这会变得有点复杂

首先,创建一个包含所有必需方法的接口。C#类将直接实现该接口,而我们将生成符合该接口的helper类

接下来,我们创建helper类:

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) } );
TypeBuilder
允许我们定义所有这些方法,接下来我们就这样做

// 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);
}
为了完整起见,这里是我使用的
IYourInterface
YourClassBase

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

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

不过,如果可以的话,我强烈建议使用文本模板在编译时生成源代码。动态代码往往很难调试(当然还有编写)。另一方面,如果您只是从模板生成这些内容,您将在代码中看到整个生成的helper类。

CodeDom可能就是您所发现的

这里有一个很好的例子:

您可以使用C#的
动态
来共享单元测试代码。假设您有一个C#类:

假设您还创建了一个实现相同接口的JavaScript对象:

scriptEngine.Execute(@"
    calculator = {
        Add: function (x, y) { return x + y; }
    };
");
您可以为以下两种方法创建一种测试方法:

public static void TestAdd(dynamic calculator) {
    Assert.AreEqual(3, calculator.Add(1, 2));
}
下面是测试这两种实现的方法:

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

这样做的好处是,您没有为每个测试调用解析和编译新的脚本代码。

CodeDom可能是您找到的。你的链接非常有用。谢谢
public class Calculator {
    public decimal Add(decimal x, decimal y) { return x + y; }
}
scriptEngine.Execute(@"
    calculator = {
        Add: function (x, y) { return x + y; }
    };
");
public static void TestAdd(dynamic calculator) {
    Assert.AreEqual(3, calculator.Add(1, 2));
}
TestAdd(new Calculator());
TestAdd(scriptEngine.Script.calculator);