Compile at runtime and invoke method using ICodeCompile and Assembly 在執行期間編譯和調用方法

.NET framework 3.5 (or is it 2.0) provide a feature to compile at runtime. And after that we can use .NET late binding technique to use the method from the source we compile at run time.
ICodeCompile is just an interface, we use a class CodeDomProvider which implements it to do the task. Let’s see how it works.
.NET framework 3.5 (可能是2.0) 提供一個執行期間編譯程式的功能. 然後我們就可以用.NET延遲繫結(動態繫結)的方式去呼叫裡面的函式.
ICodeCompile是一個介面,我們會用它的實作類別CodeDomProvider去完成編譯. 我們來看看例子.

public enum Languages
{
   VB,
   CS,
   JS
}
Languages l_theLanguageUsed = Languages.CS;
CodeDomProvider l_provider = null;
switch (l_theLanguageUsed)
{
   case Languages.CS:
      l_provider = CodeDomProvider.CreateProvider("CSharp");
      break;
   case Languages.VB:
      l_provider = CodeDomProvider.CreateProvider("VisualBasic");
      break;
   case Languages.JS:
      l_provider = CodeDomProvider.CreateProvider("JScript");
      break;
}
CompilerParameters l_compilerSettings = new CompilerParameters();
//l_compilerSettings.GenerateExecutable = false;
l_compilerSettings.GenerateInMemory = true;
l_compilerSettings.IncludeDebugInformation = false;
l_compilerSettings.TreatWarningsAsErrors = false;
l_compilerSettings.WarningLevel = 4;
//can reference other .dll
//l_compilerSettings.ReferencedAssemblies.Add();
CompilerResults m_binary = l_provider.CompileAssemblyFromSource(l_compilerSettings, theSourceCodeText);
if (m_binary.Errors.Count > 0)
{
   tb_result.Text = m_binary.Errors[0].ToString();
}
else
{
   Assembly l_assembly = m_binary.CompiledAssembly;
   Type[] l_allType = l_assembly.GetTypes();
   Type l_theClass = l_assembly.GetType("EvalClass");
   object l_instance = Activator.CreateInstance(l_theClass);
   MethodInfo l_method = l_theClass.GetMethod("Evaluate");
   tb_result.Text = l_method.Invoke(l_instance, null).ToString();
}

First of all, I define an enum to contain all language that I support. There maybe other providers but I didn’t look into that, three is enough for me. Secondly we create a provider to compile our source.
CompilerParameters is used to set compile options, you can generate binary in memory or files, choose warning level, etc. We then call Provider.CompileAssemblyFromSource(), passing the setting and source to it. We then get a compile result. It is a result not a binary because the compile may fail. But I call it a binary anyway. You can then check if the compile got any error by iterate through CompilerResults.Errors. If no error found, we can use our result to get a compiled instance, Assembly (which is really the binary). You can then use Assembly.GetTypes() to drill through the assembly, to see all its methods, classes, etc. To call a method, we need to specify the class name and method name. My testing source has a class EvalClass and it has a method Evalute() to evalute an arithmetic expression. Use Assembly.GetType(“ClassName”) to get the class, use Activator.CreateInstance() to create it. Use Type.GetMethod(“MethodName”) to get the method info. Then use MethodInfo.Invoke() to invoke the method. That’s it.

首先,我定義了一個列舉去記住我所支援的語言. .NET可能有其他語言的支援,不過我沒有深入去研究,這三個就夠我用了. 接著我們新增一個provider去編譯我們的程式碼.
CompilerParameters是用來設定編譯選項的, 你可以選擇產生執行碼到記憶體或檔案,可以選擇警告層級等.我們然後呼叫Provider.CompileAssemblyFromSource(),傳編譯選項和程式碼進去.
我們就得到一個編譯結果.它是一個結果而不是執行碼因為編譯可能會失敗.但我還是稱它為執行碼.你可以檢查CompilerResults.Errors去看看有沒有編譯錯誤.如果沒有錯誤,我們就可以用這個結果去取得執行碼, Assembly(就是真正的報行碼實體).你可以用Assembly.GetTypes()去探索這個執行碼裡有甚麼方式,類別等.要呼叫一個方法,我們要指明類別名稱和方法名稱.我的測試碼包含一個類別EvalClass,它裡面有一個方法Evalute()去解數字計算.用Assembly.GetType(“ClassName”)去取得類別,再用Activator.CreateInstance()去取得其實體,用Type.GetMethod(“MethodName”)取得方法資訊,最後用MethodInfo.Invoke()就可以調用這方法了,就這樣.

If you are trying to call a JavaScript method without a class, I tried and it won’t work even you know the method’s name or get your hand on its MethodInfo. It will tell you that the class instance is not or can not initiate. But it is good enough for me.
如果你試著呼叫沒有在任何類別裡的JavaScript方法,我試過了,這行不通,就算你知道方法名稱或者你有MethodInfo也不行,它會跟你說類別實體不能起動.但我覺得現在這樣也夠用了.

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s