網頁

2011年9月23日 星期五

dotNet Dynamic Compile Code

Last Update: 2011/09/23 16:51+08

Intro

動態編譯程式碼!!
並不是動態產生程式碼!!
目的請各位看倌自行應用
小弟是讓使用者可以自己輸入折扣/特價

底下會分幾段
1. Dynamic Compile Code
  a. Compiler Parameters
  b. Code
  c. Compile
  d. Run
2. Sample Code


Content


1. Dynamic Compile Code
底下為動態 Compile Code 並執行的方法
一開始要創建 Code Provider, 用來Compile
static object RunCode(string code){
    var provider = Microsoft.CSharp.CSharpCodeProvider.CreateProvider("C#");

    /*---Compiler Parameters---*/
    var compilerParams = new System.CodeDom.Compiler.CompilerParameters();
    compilerParams.ReferencedAssemblies.Add("System.dll");
    compilerParams.GenerateExecutable = false;
    compilerParams.GenerateInMemory = false;


    /*---Code---*/
    System.Text.StringBuilder fileCode = new StringBuilder();
    fileCode.Append("using System; \n")
        .Append("public class TestDynCode{")
            .Append("public object Run(){")
                .Append(code)
            .Append("}")
        .Append("}");


    /*---Compile---*/
    var compilerResult = provider.CompileAssemblyFromSource(compilerParams, fileCode.ToString());
    if (compilerResult.Errors.HasErrors){
        System.Text.StringBuilder error = new StringBuilder();
        foreach (System.CodeDom.Compiler.CompilerError err in compilerResult.Errors)
        { error.AppendFormat("{0}\n", err.ErrorText); }
        throw new Exception("Error Compiling Expression: " + error.ToString());
    }

    /*---Run---*/
    System.Reflection.Assembly assembly = compilerResult.CompiledAssembly;
    var obj = assembly.CreateInstance("TestDynCode");
    return obj.GetType().GetMethod("Run").Invoke(obj, null);
}

大致可以分為 4 段
跟我們平常開發的方式差不多

a. Compiler Parameters
先設置參數, 包含要加入參考的 Lib
和 Compile 環境等等參數

b. Code
開始寫Code, 內容跟平常寫Code一樣, 只是變成字串
這邊為了方便, 已經寫好 class 和 method 名稱
使用時, 只要傳來裡面的 Code

c. Compile
Compile (Build)
寫好Code當然就直接Compile看看能不能過了
可以從 compilerResult 這物件去取得 Compile Error

d. Run
取得它Compile好的Assembly
創建物件實體, 最後 Invoke Method

動態編譯Code大概就醬


2. Sample Code
這裡稍微寫一小段測試的Code
GetTotalPrice: 給價格 和 數量 算總價, 裡頭暫時寫死了計算規則
Main: 呼叫 GetTotalPrice 列出結果
static void Main(string[] args){
    Console.WriteLine(GetTotalPrice(10, 9));
    Console.WriteLine(GetTotalPrice(10, 4));
    Console.ReadKey();
}

static object GetTotalPrice(decimal price, int count){
    string inputRule = "return [數量] > 5 ? [價格] * [數量] * 0.9 : [價格] * [數量];";//賣方Input

 //實際要Run的Code
    string realInputRule = inputRule
        .Replace("[數量]", count.ToString())
        .Replace("[價格]", price.ToString());
    return RunCode(realInputRule);
}

感謝各位看倌賞臉~

2011年9月15日 星期四

XmlWriter encoding

Last Update: 2011/09/16 13:44+08

Intro

使用 XmlWriter 需要設置 Encoding 時發生的問題


Content

基本的用法是醬~
var buffer = new StringBuilder();
using (var write = System.Xml.XmlWriter.Create(buffer)){
    write.WriteStartElement("Transaction");
    write.WriteAttributeString("Action", "AskData");
    write.WriteEndElement();
}

如果要加一些參數的話, 就得設置 XmlWriterSettings
var buffer = new StringBuilder();
var setting = new System.Xml.XmlWriterSettings();
setting.Indent = true;
setting.Encoding = new System.Text.UTF8Encoding();
using (var write = System.Xml.XmlWriter.Create(buffer, setting)){
    write.WriteStartElement("Transaction");
    write.WriteAttributeString("Action", "AskData");
    write.WriteEndElement();
}

然後咱們就會發現, Indent 有作用, 但 Encoding 還是 utf-16


這是由於 dotNet 的字串本身就是utf-16編碼
所以咱們需要把 buffer 改成 Stream
var buffer = new System.IO.MemoryStream();

如果看到了如下圖的字串, 開頭有一段不明所以的空白

就是你得到了!! utf-8 的前置識別字元~請把它取消掉~

最後的Code就長醬~
var buffer = new System.IO.MemoryStream();
var setting = new System.Xml.XmlWriterSettings();
setting.Indent = true;
setting.Encoding = new System.Text.UTF8Encoding(false);//取消前置識別字元
using (var write = System.Xml.XmlWriter.Create(buffer, setting)){
    write.WriteStartElement("Transaction");
    write.WriteAttributeString("Action", "AskData");
    write.WriteEndElement();
}