利用Refly和CodeDom實現代碼的動態生成和動態編譯


CodeDom是.NET框架中比較強大也是比較難懂的對象模型,通過它可以實現.NET支持各種語言代碼的動態生成及動態編譯。我們先來看看CodeDom的定義:.NET Framework 中包含一個名為“代碼文檔對象模型”(CodeDOM) 的機制,該機制使編寫源代碼的程序的開發人員可以在運行時,根據表示所呈現代碼的單一模型,用多種編程語言生成源代碼。

Refly則是國外一個開發者對CodeDom進行封裝,目的是使得Codedom的實現更加方便易懂,和CodeDom的使用對比,代碼更加簡潔優雅,不過要了解整體的東西,也需要對CodeDOM進行詳細的了解才能熟練應用。

本人在研究學習Refly當中(詳細可以參考http://www.codeproject.com/Articles/6283/Refly-makes-the-CodeDom-er-life-easier),對其中簡單的應用有一些體會,做了一個使用Refly生成代碼的例子進行測試,並使用CodeDOM進行動態編譯。例子應該還算簡單,用來說明Refly的工作機制應該還是足夠的,同時也希望與大家探討一下進一步的應用。

生成類代碼的代碼如下所示:

            #region 生成代碼

//創建命名空間
NamespaceDeclaration ns = new NamespaceDeclaration("Demo");
ns.Imports.Add("System.Xml");
ns.Imports.Add("System.IO");
ns.Imports.Add("System.ComponentModel");
ns.Imports.Add("System.Xml.Serialization");

// 創建類定義
ClassDeclaration user = ns.AddClass("User");
//添加類說明
user.Doc.Summary.AddText("測試用戶類描述");
user.Doc.Remarks.Add("para");
user.Doc.Remarks.Into();
user.Doc.Remarks.AddText("該類是使用Refly進行生成");
user.Doc.Remarks.OutOf();

// 添加字段
FieldDeclaration name = user.AddField(typeof(string), "name");
FieldDeclaration age = user.AddField(typeof(int), "age");

// 添加構造函數(默認)
user.AddConstructor();
ConstructorDeclaration cstr = user.AddConstructor();
// 添加構造函數(參數)
ParameterDeclaration cstr_name = cstr.Signature.Parameters.Add(typeof(string), "name", true);

cstr.Body.AddAssign(Expr.This.Field(name), Expr.Arg(cstr_name));

// 添加屬性Name
PropertyDeclaration proName = user.AddProperty(name, true, true, false);
proName.Doc.Summary.AddText("用戶名稱");
//添加屬性的Attribute
AttributeDeclaration attr = proName.CustomAttributes.Add(typeof(XmlElementAttribute));
attr.Arguments.Add("ElementName", Expr.Prim(proName.Name));

// 添加屬性Age
PropertyDeclaration proAge = user.AddProperty(age, true, true, false);
proName.Doc.Summary.AddText("用戶年齡");

//添加方法
MethodDeclaration add = user.AddMethod("Add");
add.Doc.Summary.AddText("添加用戶內容");
ParameterDeclaration pName = add.Signature.Parameters.Add(typeof(string), "name", true);
add.Doc.AddParam(pName);
ParameterDeclaration pAge = add.Signature.Parameters.Add(typeof(int), "age", true);
add.Body.Add(Stm.Assign(Expr.This.Prop("Name"), Expr.Arg(pName)));
add.Body.Add(Stm.Assign(Expr.This.Prop("Age"), Expr.Arg(pAge)));

//添加方法2
MethodDeclaration show = user.AddMethod("Show");
show.Doc.Summary.AddText("輸出用戶名稱");
show.Body.Add(Expr.Snippet("Console").Method("WriteLine").Invoke(Expr.This.Prop("Name")));

// 輸出結果
Refly.CodeDom.CodeGenerator gen = new Refly.CodeDom.CodeGenerator();
gen.Provider = Refly.CodeDom.CodeGenerator.CsProvider;
gen.GenerateCode(Application.StartupPath + "/CS", ns);

#endregion

編譯代碼好像Refly沒有找到,所以用原始的CodeDOM的對象操作進行代碼的動態編譯,編譯代碼如下所示:

           #region 動態編譯代碼

string file = string.Format("{0}\\CS\\Demo\\User.cs", Application.StartupPath);
string code = FileUtil.FileToString(file);
string output = string.Format("{0}\\CS\\Demo\\User.dll", Application.StartupPath);

CSharpCodeProvider codeProvider = new CSharpCodeProvider();
CompilerParameters parameters = new CompilerParameters();
parameters.ReferencedAssemblies.Add("System.dll");
parameters.ReferencedAssemblies.Add("System.Data.dll");
parameters.ReferencedAssemblies.Add("System.Xml.dll");
parameters.GenerateInMemory = false;
parameters.TreatWarningsAsErrors = false;
parameters.OutputAssembly = output; //設定輸出文件名稱路徑

//判斷編譯結果
//CompilerResults results = codeProvider.CompileAssemblyFromSource(parameters, code);
CompilerResults results = codeProvider.CompileAssemblyFromFile(parameters, file);
if (results.Errors.HasErrors)
{
string errorMessage = "";
errorMessage = results.Errors.Count.ToString() + " Errors:";
for (int x = 0; x < results.Errors.Count; x++)
{
errorMessage = errorMessage + "\r\nLine: " +
results.Errors[x].Line.ToString() + " - " + results.Errors[x].ErrorText;
}
MessageUtil.ShowError(errorMessage);
}
//string path = results.PathToAssembly;

return results.Errors.Count == 0;

#endregion

代碼編譯了,我們使用的編譯好的類也就可以了,使用操作代碼如下所示,例子我使用反射,把生成的對象加載,並綁定到PropertyGrid控件中。

        private void btnTest_Click(object sender, EventArgs e)
{
GenerateCompile();

string assemblyFile = string.Format("{0}\\CS\\Demo\\User.dll", Application.StartupPath);
if (File.Exists(assemblyFile))
{
Assembly assObj = Assembly.LoadFile(assemblyFile);
if (assObj != null)
{
object obj = assObj.CreateInstance("Demo.User");
this.propertyGrid1.SelectedObject = obj;
}
}
}

最終例子運行的效果如下所示。

其實最終生成的User類代碼如下所示。

// Generated by Refly
namespace Demo
{
using System;
using System.Xml;
using System.IO;
using System.ComponentModel;
using System.Xml.Serialization;

/// <summary>測試用戶類描述</summary>
/// <remarks>
///<para>該類是使用Refly進行生成</para>
/// </remarks>
public class User
{

private int _age;

private string _name;

public User()
{
}

public User(string name)
{
this._name = name;
}

/// <summary>用戶名稱用戶年齡</summary>
[XmlElementAttribute(ElementName="Name")]
public virtual string Name
{
get
{
return this._name;
}
set
{
this._name = value;
}
}

public virtual int Age
{
get
{
return this._age;
}
set
{
this._age = value;
}
}

/// <summary>添加用戶內容</summary>
/// <param name="name" />
public virtual void Add(string name, int age)
{
this.Name = name;
this.Age = age;
}

/// <summary>輸出用戶名稱</summary>
public virtual void Show()
{
Console.WriteLine(this.Name);
}
}
}




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM