• 玩转动态编译:一、初识


    动态编译的好处其实有很多,但是我发现很多人其实没有真正理解或者没有灵活运用动态编译,使得这么强大的一个功能变成了鸡肋。在我自己使用的工具库中有很多地方都使用了动态编译,以后我会慢慢把工具库中的代码都发布出来,所以先把动态编译的相关知识点整理了一下

    • 什么是动态编译?

    我的个人理解就是,在程序运行期间,将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#的朋友可以在这里交流学习,分享编程的心得和快乐
  • 相关阅读:
    pip 查看软件包 可用版本并安装; pip 查看 numpy 可用版本并安装
    git submodule git 子模块管理相关操作
    git 取消文件跟踪
    C 实战练习题目1
    VC6.0设置注释快捷键
    2019-11-29-WPF-非客户区的触摸和鼠标点击响应
    2019-11-29-VisualStudio-使用新项目格式快速打出-Nuget-包
    2019-11-29-WPF-从触摸消息转触摸事件
    2019-11-29-win7-无法启动-WPF-程序-D3Dcompiler_47.dll-丢失
    2019-11-29-C#-序列类为-xml-可以使用的特性大全
  • 原文地址:https://www.cnblogs.com/blqw/p/DynamicCompile1.html
Copyright © 2020-2023  润新知