玩转动态编译:一、初识
动态编译的好处其实有很多,但是我发现很多人其实没有真正理解或者没有灵活运用动态编译,使得这么强大的一个功能变成了鸡肋。在我自己使用的工具库中有很多地方都使用了动态编译,以后我会慢慢把工具库中的代码都发布出来,所以先把动态编译的相关知识点整理了一下
- 什么是动态编译?
我的个人理解就是,在程序运行期间,将C#代码的字符串编译成为程序集对象,并通过反射该程序集调用编译后的成员。
比较容易理解的一种解释就是类似于SqlServer中的
Exec('select * from [table]')
或者Javascript中的
var a = eval("(function(){return 1;})")
- 为什么要使用动态编译?
1.为了比较方便的解决一些难题
例如,计算一个公式的字符串的值"2+3*(4-1)/5%7"
要计算这个公式的值,把他编译后直接输出无疑是最简单有效的方法,就比如这样
//formula = 2 + 3 * (4 - 1) / 5 % 7 public decimal GetValue(string formula) { string code = @" public class Class1 { public static decimal GetValue() { return (decimal)(" + formula + @"); } } "; Type type = 动态编译(code); return (decimal)type.GetMethod("GetValue").Invoke(null, null); }
上面说的这种情况是最基本的一种情况,也是最容易理解的一种情况(就我个人来说是不推荐的,因为编译一个程序集本身对资源的消耗是很大了,这种公式编译后的对象正常情况下是无法卸载的,如果动态编译只为了使用一次是极为不明智的)
2.为了程序的性能更好
3,为了程序更灵活
4,为了更好的扩展性
5,.......
ps:2,3,4这些会在下一篇文章中提到,这里先卖个关子。
- 怎么使用动态编译
先构造一个方便使用的动态编译的方法
using Microsoft.CSharp; using System; using System.CodeDom.Compiler; using System.Collections.Generic; using System.Reflection; using System.Text; namespace blqw { public class DynamicCompile_1 { /// <summary> /// /// </summary> /// <param name="code">需要编译的C#代码</param> /// <param name="usingTypes">编译代码中需要引用的类型</param> /// <returns></returns> public static Assembly CompileAssembly(string code, params Type[] usingTypes) { CompilerParameters compilerParameters = new CompilerParameters();//动态编译中使用的参数对象 compilerParameters.GenerateExecutable = false;//不需要生成可执行文件 compilerParameters.GenerateInMemory = true;//直接在内存中运行 //添加需要引用的类型 HashSet<string> ns = new HashSet<string>();//用来保存命名空间,这个对象的4.0的,如果是2.0的框架可以使用Dictionary代替 foreach (var type in usingTypes) { ns.Add("using " + type.Namespace + ";" + Environment.NewLine);//记录命名空间,因为不想重复所以使用了HashSet compilerParameters.ReferencedAssemblies.Add(type.Module.FullyQualifiedName);//这个相当于引入dll } code = string.Concat(ns) + code;//加入using命名空间的代码,即使原来已经有了也不会报错的 //声明编译器 using (CSharpCodeProvider objCSharpCodePrivoder = new CSharpCodeProvider()) { //开始编译 CompilerResults cr = objCSharpCodePrivoder.CompileAssemblyFromSource(compilerParameters, code); if (cr.Errors.HasErrors)//如果有错误 { StringBuilder sb = new StringBuilder(); sb.AppendLine("编译错误:"); foreach (CompilerError err in cr.Errors) { sb.AppendLine(err.ErrorText); } throw new Exception(sb.ToString()); } else { //返回已编译程序集 return cr.CompiledAssembly; } } } } }
再来就是调用部分的代码了,还是刚才那个计算的例子
using System; namespace blqw.DynamicCompile_Demo { public class Program { static void Main(string[] args) { decimal val = Calculate("2 + 3 * (4 - 1) / 5 % 7"); if (val == (decimal)(2 + 3 * (4 - 1) / 5 % 7)) { Console.WriteLine(val); } else { Console.WriteLine("错误"); } } public static decimal Calculate(string formula) { string code = @" public class Class1 { public static decimal GetValue() { return (decimal)(" + formula + @"); } } "; //第二个参数就是这个类中所有用到的类型,包括隐式类型 Type type = DynamicCompile_1.CompileAssembly(code, typeof(decimal)).GetType("Class1"); return (decimal)type.GetMethod("GetValue").Invoke(null, null); } } }
运行后直接就可以看到效果了,所有代码都在这里了就不提供下载了哈
期待下一篇吧
我写的文章,除了纯代码,其他的都是想表达一种思想,一种解决方案.希望各位看官不要局限于文章中的现成的代码,要多关注整个文章的主题思路,谢谢!
我发布的代码,没有任何版权,遵守WTFPL协议(如有引用,请遵守被引用代码的协议)
qq群:5946699 希望各位喜爱C#的朋友可以在这里交流学习,分享编程的心得和快乐
我发布的代码,没有任何版权,遵守WTFPL协议(如有引用,请遵守被引用代码的协议)
qq群:5946699 希望各位喜爱C#的朋友可以在这里交流学习,分享编程的心得和快乐
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· .NET周刊【3月第1期 2025-03-02】
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· [AI/GPT/综述] AI Agent的设计模式综述