最近公司由于软件业务需求,需要动态执行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();
        }
    }
}

c#动态编译字符串代码

动态生成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左右;具体没有测试过也不知道在哪指定动态编译的框架版本,如果有知道的小伙伴请在下方留言告知。
最后修改:2022 年 09 月 29 日
如果觉得我的文章对你有用,请随意赞赏