代码编译
代码编译是将外部的一段源程序进行实时编译,然后通过反射机制执行类的方法返回所需的结果。代码编译能有效提高程序的灵活度和可扩展性。使用 CodeCompiler
类可以将一段 C#/VB 代码、Code模型、或一组源文件编译成一个类或调用委托。
Fireasy 提供了代码编译的支持,位于 Fireasy.Common.Compiler
命名空间下。
1、编译委托
将一段代码编译成一个委托函数,使用委托函数传参调用得到结果。如下所示:
[TestMethod]
public void TestCompileDelegate()
{
var code = @"
using System;
using System.Drawing;
public class CalculateClass
{
// radian: 弧度
// radius: 半径
public static Point Calculate(double radian, int radius)
{
return new Point((int)(Math.Sin(radian) * radius), (int)(Math.Cos(radian) * radius));
}
}";
var compiler = new CodeCompiler();
//添加外部程序集引用
compiler.Assemblies.Add("system.drawing.dll");
var func = compiler.CompileDelegate<Func<double, int, Point>>(code, "Calculate");
var point = func(45, 100);
Assert.AreEqual(85, poing.X);
Assert.AreEqual(52, poing.Y);
}
也可以省略 CompileDelegate 方法中的第二个参数 methodName,但前提是该代码中只定义唯一一个静态公开方法。
2、编译程序集
将一段代码直接编译成一个完整的程序集。如下所示:
[TestMethod]
public void TestCompileAssembly()
{
var code = @"
using System;
using System.Drawing;
public class CalculateClass
{
// radian: 弧度
// radius: 半径
public static Point Calculate(double radian, int radius)
{
return new Point((int)(Math.Sin(radian) * radius), (int)(Math.Cos(radian) * radius));
}
public double CalculatePerimeter(int radius)
{
return 2 * Math.PI * radius;
}
}
public class Foo
{
}";
var compiler = new CodeCompiler();
//添加外部程序集引用
compiler.Assemblies.Add("system.drawing.dll");
var assembly = compiler.CompileAssembly(code);
Assert.AreEqual(2, assembly.GetExportedTypes().Length);
}
3、编译类型
如果代码中只定义了一个类,可以使用 CompileType 方法编译并返回该类。如下所示:
[TestMethod]
public void TestCompileType()
{
var code = @"
using System;
using System.Drawing;
public class CalculateClass
{
// radian: 弧度
// radius: 半径
public static Point Calculate(double radian, int radius)
{
return new Point((int)(Math.Sin(radian) * radius), (int)(Math.Cos(radian) * radius));
}
}";
var compiler = new CodeCompiler();
//添加外部程序集引用
compiler.Assemblies.Add("system.drawing.dll");
var type = compiler.CompileType(code, "CalculateClass");
Assert.IsNotNull(type);
}
4、使用对象模型
仅限于 .Net Framewor
对象模型包含代码包含的基本元素:命名空间、类型、类型成员(方法、属性、构造函数、事件等),并且包括方法实现的具体语句。CodeCompileUnit
是一个容器,逐层定义代码的结构,最后交给编译器执行。如下所示:
[TestMethod]
public void TestCompileByUnit()
{
var compiler = new CodeCompiler();
var unit = new CodeCompileUnit();
var ns = new CodeNamespace("Test");
unit.Namespaces.Add(ns);
var td = new CodeTypeDeclaration("TestClass");
ns.Types.Add(td);
var md = new CodeMemberMethod();
md.Name = "HelloWorld";
md.Attributes = MemberAttributes.Public;
td.Members.Add(md);
md.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "name"));
md.Statements.Add(new CodeSnippetStatement("return "Hello " + name + ".";"));
md.ReturnType = new CodeTypeReference(typeof(string));
var func = compiler.CompileDelegate<Func<string, string>>(unit);
Assert.AreEqual("Hello fireasy.", func("fireasy"));
}
5、使用外部源代码文件
CompileAssembly、CompileType 和 CompileDelegate 方法都有一个重载,可以指定一个以上的外部源代码文件的路径。
6、使用其他语言
仅限于 .Net Framewor
指定编译器的 CodeProvider 属性,以使用其他语言提供者来编译代码,目前 .Net Framework 只提供了 CSharpCodeProvider
和 VBCodeProvider
,默认使用 CSharpCodeProvider
。如下所示,更换为 VB 语言进行编译:
[TestMethod]
public void TestCompileByVB()
{
var code = @"
Imports System
Public Class TestClass3
Public Sub HelloWorld()
End Sub
Public Function Calcuate(ByVal x As Integer, ByVal y As Integer, ByVal r As Decimal) As Decimal
Return (x + y) * r
End Function
End Class
";
var compiler = new CodeCompiler();
compiler.CodeProvider = new VBCodeProvider();
var type = compiler.CompileType(code);
Assert.AreEqual("TestClass3", type.Name);
}
7、输出到文件
指定 OutputAssembly 属性,可以将编译的程序集输出到指定的路径。