• 关于在AppDomain中动态编译的解决方法


    看着博文之前,希望大家能看看 Allan. 的这篇文章。

    http://www.cnblogs.com/zlgcool/archive/2008/10/12/1309616.html

    之前逛园子时偶尔看到 Allan.这篇文章中提供了两种动态编译的方案,其中第一种方法由于卸载不太方便的确会导致内存方面的问题。而改良的第二种方法会将dll保存至bin目录,并通过加载这个dll,再调用dll中的方法,最后还有个删除dll的操作。这一系列操作势必会影响效率和浪费资源。我当时就想能否不简化这一系列的操作。所以才有了这篇博文,在这里首先感谢 Fish Li ,峰哥利用自己时间为园子里面的同志们解决问题,这种做法值得大家学习。

    好了,开始说正文:

    程序集的确是不能单独卸载,但程序域可以卸载,Allan. 的第二种方法可以借鉴修改。

    先看看解决方案

    autoCompiled:提供对源代码动态编译的功能

    AppDomainTest:调用程序

      Form1.cs:新建AppDomain、加载autoCompiled

      RemoteLoader.cs:提供远程访问调用

    Winform中引用autoCompiled项目。

    大致的思路就是将整个代码的编译过程(Compiled.cs类)全部拿到AppDomain中执行,RemoteLoader.cs提供远程访问。

    winform代码:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.IO;
    using System.Reflection;
    
    namespace AppDomainTest
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
    
            private void button1_Click(object sender, EventArgs e)
            {
                string strSourceCode = @"using System;
                                        namespace Test
                                        {
                                            public class HelloWorld
                                            {
                                                public string GetTime(string strName)
                                                {
                                                    return  ""你好: "" + strName + "", 现在是北京时间: "" + System.DateTime.Now.ToString();
                                                }
                                            }
                                        }";
    
                object obj = CreateAppDomainExec(strSourceCode, "Test.HelloWorld", "GetTime");
                MessageBox.Show(obj.ToString());
            }
    
            /// <summary>
            /// 编译代码
            /// </summary>
            /// <param name="strSourceCode">源代码</param>
            /// <param name="typeName">typeName,需要根据这个创建编译后的实例 namespace+className</param>
            /// <param name="FunctionName">调用的方法名</param>
            /// <returns>返回值</returns>
            public object CreateAppDomainExec(string strSourceCode, string typeName, string FunctionName)
            {
                AppDomainSetup setup = new AppDomainSetup();
                setup.ApplicationName = "TestAppDomain";
                setup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
    
                AppDomain appDomain = AppDomain.CreateDomain("CreateDomain", null, setup);
                RemoteLoader remoteLoader = (RemoteLoader)appDomain.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().GetName().FullName, typeof(RemoteLoader).FullName);//创建访问对象
                remoteLoader.LoadAssembly("autoCompiled");//加载编译代码的程序集
    
                object result = remoteLoader.CompiledExec(strSourceCode, typeName, FunctionName);//动态编译并执行
    
                //卸载程序集
                AppDomain.Unload(appDomain);
                appDomain = null;
    
                return result;
            }
        }
    }
    View Code

    RemoteLoader.cs类:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Reflection;
    
    namespace AppDomainTest
    {
        public class RemoteLoader : MarshalByRefObject
        {
            private Assembly assembly;
    
            /// <summary>
            /// 通过namespace读取
            /// </summary>
            /// <param name="fullName"></param>
            public void LoadAssembly(string fullName)
            {
                assembly = Assembly.Load(fullName);
            }
    
            /// <summary>
            /// 通过路径读取
            /// </summary>
            /// <param name="fullName"></param>
            public void LoadFromAssembly(string fullName)
            {
                assembly = Assembly.LoadFrom(fullName);
            }
    
            /// <summary>
            /// 编译代码
            /// </summary>
            /// <param name="strSourceCode">源代码</param>
            /// <param name="typeName">typeName,需要根据这个创建编译后的实例 namespace+className</param>
            /// <param name="FunctionName">调用的方法名</param>
            /// <returns>返回值</returns>
            public object CompiledExec(string SourceCode, string typeName, string FunctionName)
            {
                object objClass = assembly.CreateInstance("autoCompiled.Compiled");//获取编译方法
                object strResult = objClass.GetType().InvokeMember("GetCompiledByString", BindingFlags.InvokeMethod, null, objClass, new object[] { SourceCode, typeName, FunctionName });
                return strResult;
            }
        }  
    }
    View Code

    Compiled.cs类:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.CodeDom.Compiler;
    using Microsoft.CSharp;
    using System.Reflection;
    
    namespace autoCompiled
    {
        public class Compiled
        {
            /// <summary>
            /// 编译代码
            /// </summary>
            /// <param name="strSourceCode">源代码</param>
            /// <param name="typeName">typeName,需要根据这个创建编译后的实例 namespace+className</param>
            /// <param name="FunctionName">调用的方法名</param>
            /// <returns>返回值</returns>
            public object GetCompiledByString(string strSourceCode,string typeName,string FunctionName)
            {
                CompilerParameters objCompilerParameters = new CompilerParameters();
                objCompilerParameters.ReferencedAssemblies.Add("System.dll");
                objCompilerParameters.GenerateInMemory = true;
    
                CSharpCodeProvider objCSharpCodePrivoder = new CSharpCodeProvider();
                CompilerResults cr = objCSharpCodePrivoder.CompileAssemblyFromSource(objCompilerParameters, strSourceCode);
                Assembly objAssembly = cr.CompiledAssembly;
    
                object objClass = objAssembly.CreateInstance(typeName);//获取方法 namespace.className
                if (objClass == null) return "获取类失败";
    
                //反射调用方法
                object objResult = objClass.GetType().InvokeMember(FunctionName, BindingFlags.InvokeMethod, null, objClass, new object[] { "Tsong" });
                return objResult;
            }
        }
    }
    View Code

    执行步骤:

    1:点击按钮之后,代码会新建一个程序域AppDomain

    2:在新建立的ApppDomain中程序域加载程序集【remoteLoader.LoadAssembly("autoCompiled");】

    3:获取程序集中的需要调用的类 【object objClass = assembly.CreateInstance("autoCompiled.Compiled");】

    4:调用类方法进行编译并返回执行结果【GetCompiledByString】

    5:卸载AppDomain

    在AppDomain.Unload之后再次调用,会发现程序报错。说明程序集的确已随着AppDomain被卸载。

    这种方法的确可以避免Allan.第二种方法繁琐的步骤,但是本质上还是通过加载编译后的引用。每次都会通过2次反射调用方法

    第一次:调用引用的方法

    第二次:调用自动编译代码中的方法

    本人理想化的只是将动态编译之后的结果 Assembly 添加至AppDomain中,而不是将代码编译的过程都添加至AppDomain中。但由于水平有限,希望有大神可以指教

  • 相关阅读:
    ps:点阵格式图像
    ps:图像尺寸
    ps:HSB色彩模式
    git上传文件夹的问题
    sublime下载emmet
    git Octotree:提供项目目录,方便用户在线快速浏览项目结构【转载】
    git@github.com出现Permission denied (publickey)
    less:避免编译
    less:@arguments变量
    less嵌套规则
  • 原文地址:https://www.cnblogs.com/Tsong/p/3091177.html
Copyright © 2020-2023  润新知