动态编译和执行条件语句

时间:2013-04-22 22:13:01

标签: c# .net roslyn dynamic-compilation

我们希望为用户提供图形格式来设计某些数据的条件语句。我们的应用程序将采用该图形格式,将其转换为C#,编译它,并针对某些数据运行条件语句,返回一个布尔值。

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

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

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

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

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

3 个答案:

答案 0 :(得分:3)

  

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

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

  

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

不是真的。您无法在.Net中卸载普通程序集。一旦检测到它没有被使用,可以卸载可收集的程序集,但这仅适用于动态程序集(不是从磁盘加载的程序集)。您还可以卸载AppDomain,它也会卸载加载到该域中的所有程序集,但这意味着在单独的AppDomain中运行您的语句。

  

此外,生成C#if语句比LINQ表达式树更容易。

这真的重要吗?您只需要编写一次该代码。而且我不认为创建if语句实际上就更难以表达树,一旦你知道如何去做。特别是与Roslyn相比,Roslyn在用于代码生成时非常冗长(因为它不是它的主要用例)。

  据报道,[Roslyn]比CodeDOM更快,但Roslyn仍在使用CTP。

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

  

如果每个.dll都有自己独特的命名空间......

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

答案 1 :(得分:0)

只是一些额外的注释(关于@svick -s的答案,我最多的第二个),不同的角度......

  1. 如果可以,我会说要与Roslyn一起去 - 取决于你的目标。你会得到一个满足你所需要的下一代工具和一个真正的'编译服务'作为营销:),但是说真的。

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

  3. 如果您有更多'任意'用户代码来运行 - 或者使用C#完整'文件' - 这意味着您必须非常模仿C# - 然后使用CodeDom 。 CodeDOM是可靠的,它可以工作,但我会按此顺序进行。

答案 2 :(得分:0)

完成任何事情都是微不足道的,但您可能想要考虑通过自己发布IL来动态创建内存中的程序集。

可以找到here的例子。你会做这样的事情:

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;

希望这有帮助。