动态编译和执行条件语句

本文关键字:条件 语句 执行 编译 动态 | 更新日期: 2023-09-27 18:10:27

我们想给用户一种图形化的格式来设计一些数据上的条件语句。我们的应用程序将采用图形格式,将其转换为c#,对其进行编译,然后针对一些数据运行条件语句,返回一个布尔值。

问题在于,这些条件语句需要在运行时编写和编译(当然还有执行),因为我们不会在每次用户创建新的条件语句时都重新构建应用程序。

我们考虑过使用LINQ表达式树,但是编译后的LINQ表达式树无法保存,这意味着我们需要在每次执行条件语句时重新编译。

我们认为更好的替代方法是使用CodeDOM将条件语句编译为.dll(它们将被转换为静态类的静态方法,该静态类接受作为参数的数据来运行条件语句)。这允许我们保存编译后的语句,并且可以在运行时加载和卸载.dll。而且,生成c# if语句比生成LINQ表达式树更容易。

或者,我们可以使用Roslyn来生成.dll。据报道,这比CodeDOM快,但Roslyn仍在CTP中。

是否有隐藏的陷阱,或者这样做的一般模式,我们应该知道?除了非常小心地只生成针对数据进行测试的函数(并且不修改数据或允许调用任何其他函数)之外,我们还应该注意什么?加载和卸载(可能有数百个)这些.dll会导致问题吗?如果每个.dll都有自己唯一的名称空间,那么加载和卸载(可能有数百个)它们会留下工件吗?

动态编译和执行条件语句

我们需要在每次执行条件语句时重新编译

我认为这不会是一个问题。除非你需要每秒编译很多表达式,否则编译表达式树对性能的影响不会太明显。

我们可以在运行时加载和卸载。dll

没有。不能卸载. net中的普通程序集。一旦检测到一个可收集程序集没有被使用,它将被卸载,但这只适用于动态程序集(不包括从磁盘加载的程序集)。你也可以卸载一个AppDomain,它也会卸载加载到该域中的所有程序集,但这意味着在一个单独的AppDomain中运行你的语句。

而且,生成c# if语句比生成LINQ表达式树更容易。

这真的重要吗?您只需要编写一次代码。我不认为用表达式树创建if语句有多难,只要你知道怎么做。特别是当与Roslyn相比时,它在用于代码生成时非常冗长(因为这不是它的主要用例)。

[Roslyn]据说比CodeDOM更快,但Roslyn仍在CTP中。

你能引用一个来源吗?但是我真的怀疑编译的速度对你来说真的很重要。

如果每个。dll都有自己唯一的命名空间…

这没有任何意义,DLL没有命名空间。实际上,CLR并不真正处理名称空间,它只是看到一个类的名称中有点。

只是一些额外的注意(关于@svick -s的答案,我对它的大部分),不同的角度…

  1. 如果可以的话,我会说和Roslyn一起去 -这取决于你的目标。你会得到一个满足你需要的新一代工具和一个真正的"编译器服务",但说真的。

  2. 如果你没有广泛的表达式树来构建 -即有限的集合-我建议将其作为第二个选择。你不能用CodeDom卸载dll,除非通过AppDomain,这是"沟通"的痛苦(基本上是跨进程)。

  3. 如果你有更多的"任意"用户代码来运行 -或者c#的完整"文件"-这意味着你必须模仿c# -然后使用CodeDom。CodeDOM是可靠的,它可以工作,但我要按照这个顺序。

这不是一件微不足道的事情,但是您可能需要考虑通过自己发出IL来动态地在内存中创建程序集。

这个例子可以在这里找到。您将执行如下操作:

AppDomain domain = Thread.GetDomain();
// create a new assembly for the proxy
AssemblyBuilder assemblyBuilder = 
    domain.DefineDynamicAssembly(
        new AssemblyName("ProxyAssembly"), 
            AssemblyBuilderAccess.Run);
// create a new module for the proxy
ModuleBuilder moduleBuilder = 
    assemblyBuilder.DefineDynamicModule("ProxyModule", true);
// Set the class to be public and sealed
TypeAttributes typeAttributes = 
    TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed;
// Construct the type builder
TypeBuilder typeBuilder = 
    moduleBuilder.DefineType(typeof(TInterface).Name 
    + "Proxy", typeAttributes, channelType);
List<Type> allInterfaces = new List<Type>(typeof(TInterface).GetInterfaces());
allInterfaces.Add(typeof(TInterface));
//add the interface
typeBuilder.AddInterfaceImplementation(typeof(TInterface));
//construct the constructor
Type[] ctorArgTypes = new Type[] { ctorArgType };
CreateConstructor(channelType, typeBuilder, ctorArgTypes);
//...
//construct the method builders from the method infos defined in the interface
List<MethodInfo> methods = GetAllMethods(allInterfaces);
foreach (MethodInfo methodInfo in methods)
{
    MethodBuilder methodBuilder = 
        ConstructMethod(channelType, methodInfo, typeBuilder, 
            ldindOpCodeTypeMap, stindOpCodeTypeMap);
    typeBuilder.DefineMethodOverride(methodBuilder, methodInfo);
}
//create the type and construct an instance
Type t = typeBuilder.CreateType();
TInterface instance = 
    (TInterface)t.GetConstructor(ctorArgTypes).Invoke(
        new object[] { channelCtorValue });
return instance;