最近公司由于软件业务需求,需要动态执行c#字符串代码以适应业务需求,后面各种找资料终于找到几种靠谱的方式,在此记录一下。
目前本人了解到动态执行代码的方式大概两种,第一种是c#自带的CSharpCodeProvider方式,第二种是第三方脚本引擎cs-script;本文就先介绍下用原生的方式动态执行c#代码,如果需要动态编译成exe或dll文件的,请看最后面;例子完整可用,如果遇到问题,请留言。
首先来介绍下原生的方式如何实现动态编译与执行c#代码。
using Microsoft.CSharp;
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace DynamicExcute
{
/// <summary>
/// 动态执行c#代码帮助类
/// </summary>
public class ExecuteCompiler
{
public static void Execute()
{
CSharpCodeProvider objCSharpCodePrivoder = new CSharpCodeProvider();
ICodeCompiler objICodeCompiler = objCSharpCodePrivoder.CreateCompiler();
CompilerParameters objCompilerParameters = new CompilerParameters();
//添加依赖的dll
objCompilerParameters.ReferencedAssemblies.Add("System.dll");
objCompilerParameters.GenerateExecutable = false;
objCompilerParameters.GenerateInMemory = true;
//开始编译代码
CompilerResults cr = objICodeCompiler.CompileAssemblyFromSource(objCompilerParameters, GenCode());
if (cr.Errors.HasErrors)
{
Console.WriteLine("编译失败,详情请查看错误输出");
foreach (CompilerError err in cr.Errors)
{
Console.WriteLine(err.ErrorText);
}
}
else
{
//通过反射,调用编译完成的实例
Assembly objAssembly = cr.CompiledAssembly;
object objHelloWorld = objAssembly.CreateInstance("Dyn.MyDyn");
MethodInfo objMI = objHelloWorld.GetType().GetMethod("DynMethod");
Console.WriteLine(objMI.Invoke(objHelloWorld, null));
}
Console.ReadLine();
}
private static string GenCode()
{
StringBuilder sb = new StringBuilder();
sb.Append("using System;");
sb.Append("namespace Dyn");
sb.Append("{");
sb.Append(" public class MyDyn");
sb.Append(" {");
sb.Append(" public string DynMethod()");
sb.Append(" {");
sb.Append(" return \"动态编译成功了,一缕清风\";");
sb.Append(" }");
sb.Append(" }");
sb.Append("}");
string code = sb.ToString();
//Console.WriteLine(code);
//Console.WriteLine();
return code;
}
}
}
调用结果
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DynamicExcute
{
class Program
{
static void Main(string[] args)
{
ExecuteCompiler.Execute();
Console.ReadKey();
}
}
}
动态生成exe文件或dll
- 动态生成exe时需要设置exe类型,即控制台程序或windows应用程序,也可以设置exe图标,资源等;
- 生成dll文件时,图标无需设置,然后生成类型设置为/target:library,具体类型参阅微软文档
/// <summary>
/// 动态编译成文件、exe或者dll
/// </summary>
public static void Gen2File()
{
CSharpCodeProvider p = new CSharpCodeProvider();
// 设置编译参数
CompilerParameters options = new CompilerParameters();
//加入引用的程序集
options.ReferencedAssemblies.Add("System.dll");
options.ReferencedAssemblies.Add("System.Xml.dll");
options.ReferencedAssemblies.Add("System.Xml.Linq.dll");
options.ReferencedAssemblies.Add("System.Data.dll");
options.ReferencedAssemblies.Add("System.Data.DataSetExtensions.dll");
options.ReferencedAssemblies.Add("System.Drawing.dll");
options.ReferencedAssemblies.Add("System.Core.dll");
options.ReferencedAssemblies.Add("System.Web.dll");
options.ReferencedAssemblies.Add("Microsoft.CSharp.dll");
options.ReferencedAssemblies.Add("System.Windows.Forms.dll"); //是否生成exe文件,true表示生成exe,false表示生成dll文件
options.GenerateExecutable = true;
//加入外部资源文件,不支持文件夹,因此多个文件可以自己压缩为zip再加入或者循环加入
//options.EmbeddedResources.Add(AppDomain.CurrentDomain.BaseDirectory + "test.dat");
//dll文件图标可不设置。
options.CompilerOptions = "/target:exe /optimize /win32icon:update.ico ";
//编译后文件路径与名称
options.OutputAssembly = AppDomain.CurrentDomain.BaseDirectory + "\\mydyn.exe";
//为exe设置主程序入口,若生成dll文件此选项可不设置。
options.MainClass = "DynTest.Program";
//开始编译代码
CompilerResults cr = p.CompileAssemblyFromSource(options, GenFileCode());
if (cr.Errors.HasErrors)
{
Console.WriteLine("编译失败,详情请查看错误输出");
foreach (CompilerError err in cr.Errors)
{
Console.WriteLine(err.ErrorText);
}
}
}
private static string GenFileCode()
{
StringBuilder sb = new StringBuilder();
sb.Append("using System;");
sb.Append("using System.Runtime.InteropServices;");
sb.Append("using System.Reflection;");
//程序集信息,可选设置。
sb.Append("[assembly: AssemblyTitle(\"DynTest\")]");
sb.Append("[assembly: AssemblyDescription(\"\")]");
sb.Append("[assembly: AssemblyConfiguration(\"\")]");
sb.Append("[assembly: AssemblyCompany(\"\")]");
sb.Append("[assembly: AssemblyProduct(\"DynTest\")]");
sb.Append("[assembly: AssemblyCopyright(\"Copyright © 2021\")]");
sb.Append("[assembly: AssemblyTrademark(\"\")]");
sb.Append("[assembly: AssemblyCulture(\"\")]");
sb.Append("[assembly: ComVisible(false)]");
sb.Append("[assembly: AssemblyVersion(\"1.0.0.0\")]");
sb.Append("[assembly: AssemblyFileVersion(\"1.0.0.0\")]");
sb.Append("namespace DynTest");
sb.Append("{");
sb.Append("class Program");
sb.Append("{");
sb.Append("static void Main(string[] args)");
sb.Append("{");
sb.Append(" Console.WriteLine(\"这是一个动态编译生成的exe\");");
sb.Append("Console.ReadKey();");
sb.Append("}");
sb.Append("}");
sb.Append("}");
return sb.ToString();
}
结语
- 原生的方式动态编译到这里就讲完了,下一篇文章讲如何用csc-scrpit脚本引擎动态编译。
- 原生的方式编译的exe或者dll有.netframework版本的限制,好像是2.0-3.5左右;具体没有测试过也不知道在哪指定动态编译的框架版本,如果有知道的小伙伴请在下方留言告知。
此处评论已关闭