通過Emit實現動態類產生

來源:互聯網
上載者:User
動態 動態產生一個類對於AOP,O/R Mapping等技術非常有協助。對於Java來說,問題不大,而對於.NET,則要麻煩些(主要麻煩在於實現代碼的產生需要IL),故猜測這可能也是在AOP, O/R Mapping方面,Java走得略前的原因吧。

麻煩歸麻煩,非不能也,動態產生一個簡單的類還不至於太難。

假設有如下介面:
interface IAnimal
{
void move();
void eat();
}

希望能建立一個類產生器TypeCreator,並能以以下方式使用:

TypeCreator tc=new TypeCreator(typeof(IAnimal));
Type t = tc.build();
IAnimal myAnimal= (IAnimal)Activator.CreateInstance(t);
myAnimal.move();
myAnimal.eat();
首先,發現System.Reflection.Emit.TypeBuilder似乎就是一個現成的類產生器。 不過TypeBuilder既沒有實用的static方法,也不能在外部執行個體化。不過ModuleBuilder倒有一個DefineType()方法,可以得到TypeBuilder;而ModuleBuilder和TyperBuilder一個德行,不能直接建立,得從AssemblyBuilder的DefineDynamicModule()方法得到。追根溯源,AssemblyBuilder得從AppDomain的DefineDynamicAssembly()的得來。最終好在AppDomain提供了一個靜態方法:AppDomain.CurrentDomain. 這一連串並非沒有道理,類型是依附於Module的,而Module依附於Assembly,而Assembly則被AppDomain裝載。所謂“皮之不存,毛將焉附”,為了建立Type這個“毛”,得先把Assembly,Module這些“皮”依次構造出來:

using System;
using System.Reflection;
using System.Reflection.Emit;


public class TypeCreator
{
private Type targetType;

/// <summary>
/// 建構函式
/// </summary>
/// <param name="targetType">被實現或者繼承的類型</param>
public TypeCreator(Type targetType)
{
this.targetType = targetType;
}

public Type build()
{
//擷取當前AppDomain
AppDomain currentAppDomain = AppDomain.CurrentDomain;

//System.Reflection.AssemblyName 是用來表示一個Assembly的完整名稱的
AssemblyName assyName = new AssemblyName();

//為要建立的Assembly定義一個名稱(這裡忽略版本號碼,Culture等資訊)
assyName.Name = "MyAssyFor_" + targetType.Name;

//擷取AssemblyBuilder
//AssemblyBuilderAccess有Run,Save,RunAndSave三個取值
AssemblyBuilder assyBuilder = currentAppDomain.DefineDynamicAssembly(assyName,AssemblyBuilderAccess.Run);

//擷取ModuleBuilder,提供String參數作為Module名稱,隨便設一個
ModuleBuilder modBuilder = assyBuilder.DefineDynamicModule("MyModFor_"+targetType.Name);

//新類型的名稱:隨便定一個
String newTypeName = "Imp_"+targetType.Name;

//新類型的屬性:要建立的是Class,而非Interface,Abstract Class等,而且是Public的
TypeAttributes newTypeAttribute = TypeAttributes.Class | TypeAttributes.Public;

//聲明要建立的新類型的父類型
Type newTypeParent;

//聲明要建立的新類型要實現的介面
Type[] newTypeInterfaces;

//對於基底類型是否為介面,作不同處理
if(targetType.IsInterface)
{
newTypeParent = null;
newTypeInterfaces = new Type[]{targetType};
}
else
{
newTypeParent = targetType;
newTypeInterfaces = new Type[0];
}

//得到類型產生器
TypeBuilder typeBuilder = modBuilder.DefineType(newTypeName,newTypeAttribute,newTypeParent,newTypeInterfaces);

//以下將為新型別宣告方法:新類型應該override基底類型的所以virtual方法

//得到基底類型的所有方法
MethodInfo[] targetMethods = targetType.GetMethods();

//遍曆各個方法,對於Virtual的方法,擷取其簽名,作為新類型的方法
foreach(MethodInfo targetMethod in targetMethods)
{
//只挑出virtual的方法
if(targetMethod.IsVirtual)
{
//得到方法的各個參數的類型
ParameterInfo[] paramInfo = targetMethod.GetParameters();
Type[] paramType = new Type[paramInfo.Length];
for(int i=0;i<paramInfo.Length;i++)
paramType[i] = paramInfo[i].ParameterType;

//傳入方法簽名,得到方法產生器
MethodBuilder methodBuilder = typeBuilder.DefineMethod(targetMethod.Name,MethodAttributes.Public|MethodAttributes.Virtual,targetMethod.ReturnType,paramType);

//由於要產生的是具體類,所以方法的實現是必不可少的。而方法的實現是通過Emit IL代碼來產生的

//得到IL產生器
ILGenerator ilGen = methodBuilder.GetILGenerator();
//以下三行相當於:{Console.Writeln("I'm "+ targetMethod.Name +"ing");}
ilGen.Emit(OpCodes.Ldstr,"I'm "+ targetMethod.Name +"ing");
ilGen.Emit(OpCodes.Call,typeof(Console).GetMethod("WriteLine",new Type[]{typeof(String)}));
ilGen.Emit(OpCodes.Ret);
}
}
//真正建立,並返回
return(typeBuilder.CreateType());
}
}

好了,測試一下試試看:using System;

public class Tester
{
public static void Main(String[] args)
{
TypeCreator tc=new TypeCreator(typeof(IAnimal));
Type t = tc.build();
IAnimal animal= (IAnimal)Activator.CreateInstance(t);
animal.move();
animal.eat();

Console.Read ();
}
}

得到輸出:I'm moveingI'm eating 總結:如果用於AOP的話,Emit可以動態產生一個裝飾類,相比於基於Remoting架構的TP/RP的方法,效率可能要高些,而且還能攔截new操作符。缺點:對於非Virtual的方法,似乎無法攔截。用於O/R Mapping的類產生,倒是不錯



相關文章

E-Commerce Solutions

Leverage the same tools powering the Alibaba Ecosystem

Learn more >

Apsara Conference 2019

The Rise of Data Intelligence, September 25th - 27th, Hangzhou, China

Learn more >

Alibaba Cloud Free Trial

Learn and experience the power of Alibaba Cloud with a free trial worth $300-1200 USD

Learn more >

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。