熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> .NET編程 >> 正文

詳解.NET中的動態編譯

2013-11-13 09:46:59  來源: .NET編程 

  代碼的動態編譯並執行是一個NET平台提供給我們的很強大的工具用以靈活擴展(當然是面對內部開發人員)復雜而無法估算的邏輯並通過一些額外的代碼來擴展我們已有 的應用程序這在很大程度上給我們提供了另外一種擴展的方式(當然這並不能算是嚴格意義上的擴展但至少為我們提供了一種思路)

  動態代碼執行可以應用在諸如模板生成外加邏輯擴展等一些場合一個簡單的例子為了網站那的響應速度HTML靜態頁面往往是我們最好的選擇但基於數據驅動的網站往往又很難用靜態頁面實現那麼將動態頁面生成html的工作或許就是一個很好的應用場合另外對於一些模板的套用我們同樣可以用它來做另外這本身也是插件編寫的方式

  最基本的動態編譯

  Net為我們提供了很強大的支持來實現這一切我們可以去做的基礎主要應用的兩個命名空間是SystemCodeDomCompiler和MicrosoftCSharp或MicrosoftVisualBasic另外還需要用到反射來動態執行你的代碼動態編譯並執行代碼的原理其實在於將提供的源代碼交予CSharpCodeProvider來執行編譯(其實和CSC沒什麼兩樣)如果沒有任何編譯錯誤生成的IL代碼會被編譯成DLL存放於於內存並加載在某個應用程序域(默認為當前)內並通過反射的方式來調用其某個方法或者觸發某個事件等之所以說它是插件編寫的一種方式也正是因為與此我們可以通過預先定義好的借口來組織和擴展我們的程序並將其交還給主程序去觸發一個基本的動態編譯並執行代碼的步驟包括

  ·         將要被編譯和執行的代碼讀入並以字符串方式保存

  ·         聲明CSharpCodeProvider對象實例

  ·         調用CSharpCodeProvider實例的CompileAssemblyFromSource方法編譯

  ·         用反射生成被生成對象的實例(AssemblyCreateInstance)

  ·         調用其方法

  以下代碼片段包含了完整的編譯和執行過程

    

  //get the code to compile

  string strSourceCode = thistxtSourceText;

   

  // Create a new CSharpCodePrivoder instance

  CSharpCodeProvider objCSharpCodePrivoder = new CSharpCodeProvider();

   

  // Sets the runtime compiling parameters by crating a new CompilerParameters instance

  CompilerParameters objCompilerParameters = new CompilerParameters();

  objCompilerParametersReferencedAssembliesAdd(Systemdll);

  objCompilerParametersReferencedAssembliesAdd(SystemWindowsFormsdll);

  objCompilerParametersGenerateInMemory = true;

   

  // CompilerResults: Complile the code snippet by calling a method from the provider

  CompilerResults cr = objCSharpCodePrivoderCompileAssemblyFromSource(objCompilerParameters strSourceCode);

   

  if (crErrorsHasErrors)

  {

  string strErrorMsg = crErrorsCountToString() + Errors:;

   

  for (int x = ; x < crErrorsCount; x++)

  {

  strErrorMsg = strErrorMsg + \r\nLine: +

  crErrors[x]LineToString() + +

  crErrors[x]ErrorText;

  }

   

  thistxtResultText = strErrorMsg;

  MessageBoxShow(There were build erros please modify your code Compiling Error);

   

  return;

  }

   

  // Invoke the method by using Reflection

  Assembly objAssembly = crCompiledAssembly;

  object objClass = objAssemblyCreateInstance(DynamiclyHelloWorld);

  if (objClass == null)

  {

  thistxtResultText = Error: + Couldnt load class;

  return;

  }

   

  object[] objCodeParms = new object[];

  objCodeParms[] = Allan;

   

  string strResult = (string)objClassGetType()InvokeMember(

  GetTime BindingFlagsInvokeMethod null objClass objCodeParms);

   

  thistxtResultText = strResult;

  需要解釋的是這裡我們在傳遞編譯參數時設置了GenerateInMemory為true這表明生成的DLL會被加載在內存中(隨後被默認引用入當前應用程序域)在調用GetTime方法時我們需要加入參數傳遞object類型的數組並通過Reflection的InvokeMember來調用在創建生成的Assembly中的對象實例時需要注意用到的命名空間是你輸入代碼的真實命名空間以下是我們輸入的測試代碼(為了方便所有的代碼都在外部輸入動態執行時不做調整)

    

  namespace Dynamicly

  {

  public class HelloWorld

  {

  public string GetTime(string strName)

  {

  return  Welcome + strName + Check in at + SystemDateTimeNowToString();

  }

  }

  }

  運行附件中提供的程序可以很容易得到一下結果

  

  改進的執行過程

  現在一切看起來很好我們可以編譯代碼並把代碼加載到當前應用程序域中來參與我們的活動但你是否想過去卸載掉這段程序呢?更好的去控制程序呢?另外當你運行這個程序很多遍的時候你會發現占用內存很大而且每次執行都會增大內存使用是否需要來解決這個問題呢?當然需要否則你會發現這個東西根本沒用我需要執行的一些大的應用會讓我的服務器crzay不堪重負而瘋掉的

  要解決這個問題我們需要來了解一下應用程序域NET Application Domain是NET提供的運行和承載一個活動的進程(Process)的容器它將這個進程運行所需的代碼和數據隔離到一個小的范圍內稱為Application Domain當一個應用程序運行時Application Domains將所有的程序集/組件集加載到當前的應用程序域中並根據需要來調用而對於動態生成的代碼/程序集我們看起來好像並沒有辦法去管理它其實不然我們可以用Application Domain提供的管理程序集的辦法來動態加載和移除Assemblies來達到我們的提高性能的目的具體怎麼做呢在前邊的基礎上增加以下步驟

  ·         創建另外一個Application Domain

  ·         動態創建(編譯)代碼並保存到磁盤

  ·         創建一個公共的遠程調用接口

  ·         創建遠程調用接口的實例並通過這個接口來訪問其方法

  換句話來講就是將對象加載到另外一個AppDomain中並通過遠程調用的方法來調用所謂遠程調用其實也就是跨應用程序域調用所以這個對象(動態代碼)必須繼承於MarshalByRefObject類為了復用這個接口被單獨提到一個工程中並提供一個工廠來簡化每次的調用操作

    

  using System;

  using SystemCollectionsGeneric;

  using SystemLinq;

  using SystemText;

  using SystemReflection;

   

  namespace RemoteAccess

  {

  /// <summary>

  /// Interface that can be run over the remote AppDomain boundary

  /// </summary>

  public interface IRemoteInterface

  {

  object Invoke(string lcMethodobject[] Parameters);

  }

   

  /// <summary>

  /// Factory class to create objects exposing IRemoteInterface

  /// </summary>

  public class RemoteLoaderFactory : MarshalByRefObject

  {

  private const BindingFlags bfi = BindingFlagsInstance | BindingFlagsPublic | BindingFlagsCreateInstance;

   

  public RemoteLoaderFactory() {}

   

  public IRemoteInterface Create( string assemblyFile string typeName object[] constructArgs )

  {

  return (IRemoteInterface) ActivatorCreateInstanceFrom(

  assemblyFile typeName false bfi null constructArgs

  null null null )Unwrap();

  }

  }       

  }

  接下來在原來基礎上需要修改的是

  ·         將編譯成的DLL保存到磁盤中

  ·         創建另外的AppDomain

  ·         獲得IRemoteInterface接口的引用(將生成的DLL加載到額外的AppDomain)

  ·         調用InvokeMethod方法來遠程調用

  ·         可以通過AppDomainUnload()方法卸載程序集

  以下是完整的代碼演示了如何應用這一方案

    

  //get the code to compile

  string strSourceCode = thistxtSourceText;

   

  // Create an addtional AppDomain

  AppDomainSetup objSetup = new AppDomainSetup();

  objSetupApplicationBase = AppDomainCurrentDomainBaseDirectory;

  AppDomain objAppDomain = AppDomainCreateDomain(MyAppDomain null objSetup);

   

  // Create a new CSharpCodePrivoder instance

  CSharpCodeProvider objCSharpCodePrivoder = new CSharpCodeProvider();

   

  // Sets the runtime compiling parameters by crating a new CompilerParameters instance

  CompilerParameters objCompilerParameters = new CompilerParameters();

  objCompilerParametersReferencedAssembliesAdd(Systemdll);

  objCompilerParametersReferencedAssembliesAdd(SystemWindowsFormsdll);

   

  // Load the remote loader interface

  objCompilerParametersReferencedAssembliesAdd(RemoteAccessdll);

   

  // Load the resulting assembly into memory

  objCompilerParametersGenerateInMemory = false;

  objCompilerParametersOutputAssembly = DynamicalCodedll;

   

  // CompilerResults: Complile the code snippet by calling a method from the provider

  CompilerResults cr = objCSharpCodePrivoderCompileAssemblyFromSource(objCompilerParameters strSourceCode);

   

  if (crErrorsHasErrors)

  {

  string strErrorMsg = crErrorsCountToString() + Errors:;

   

  for (int x = ; x < crErrorsCount; x++)

  {

  strErrorMsg = strErrorMsg + \r\nLine: +

  crErrors[x]LineToString() + +

  crErrors[x]ErrorText;

  }

   

  thistxtResultText = strErrorMsg;

  MessageBoxShow(There were build erros please modify your code Compiling Error);

   

  return;

  }

   

  // Invoke the method by using Reflection

  RemoteLoaderFactory factory = (RemoteLoaderFactory)objAppDomainCreateInstance(RemoteAccessRemoteAccessRemoteLoaderFactory)Unwrap();

   

  // with help of factory create a real LiveClass instance

  object objObject = factoryCreate(DynamicalCodedll DynamiclyHelloWorld null);

   

  if (objObject == null)

  {

  thistxtResultText = Error: + Couldnt load class;

  return;

  }

   

  // *** Cast object to remote interface avoid loading type info

  IRemoteInterface objRemote = (IRemoteInterface)objObject;

   

  object[] objCodeParms = new object[];

  objCodeParms[] = Allan;

   

  string strResult = (string)objRemoteInvoke(GetTime objCodeParms);

   

  thistxtResultText = strResult;

   

  //Dispose the objects and unload the generated DLLs

  objRemote = null;

  AppDomainUnload(objAppDomain);

   

  SystemIOFileDelete(DynamicalCodedll);

  對於客戶端的輸入程序我們需要繼承於MarshalByRefObject類和IRemoteInterface接口並添加對RemoteAccess程序集的引用以下為輸入

    

  using System;

  using SystemReflection;

  using RemoteAccess;

   

  namespace Dynamicly

  {

  public class HelloWorld : MarshalByRefObjectIRemoteInterface

  {

  public object Invoke(string strMethodobject[] Parameters)

  {

  return thisGetType()InvokeMember(strMethod BindingFlagsInvokeMethodnullthisParameters);

  }

   

  public string GetTime(string strName)

  {

  return  Welcome + strName + Check in at + SystemDateTimeNowToString();

  }

  }

  }

  這樣你可以通過適時的編譯加載和卸載程序集來保證你的程序始終處於一個可控消耗的過程並且達到了動態編譯的目的而且因為在不同的應用程序域中讓你的本身的程序更加安全和健壯示例代碼下載


From:http://tw.wingwit.com/Article/program/net/201311/11574.html
    推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.