日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

基于动态代码生成技术的动态对象工厂

發布時間:2025/4/5 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 基于动态代码生成技术的动态对象工厂 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
C#中所有的引用類型的實例都需要在運行時動態創建,創建對象實例最常見的辦法就是使用new操作符,使用new操作符就需要在編譯器明確的知道要創建的對象的類型,如果在編譯器并不能明確,就需要用到反射技術,例如: ????????????String?className?=?"MyNamesapce.MyClass";
????????????ConstructorInfo?ci?
=?Type.GetType(className).GetConstructor(new?Type[0]);
????????????Object?o1?
=?ci.Invoke();
????????????Object?o2?
=?Activator.CreateInstance(Type.GetType(className); 上述代碼展示了兩種基于反射的動態對象創建,但這種方法的效率是比較低下的,特別是在需要大量的動態創建實例的時候。為此我們需要一種更為高效的動態創建實例的方法,動態代碼生成就是一種不錯的方式。 之所以不能直接使用new,就是因為new后面的類型參數在編譯器是不知道的,那么就需要在運行的時候動態的創建出與new相配合的代碼。這類似于在Javascript中使用eval函數: var?className?=?“MyClass”;
????????
var?myObj?=?eval(“new?“?+?className);
C#并沒有像eval這樣的函數,畢竟編譯型語言和腳本語言是不同的,所以要實現類似的功能,就要使用到System.Reflection.Emit名空間下的類來動態的創建出可執行的代碼。首先需要認識幾個涉及到的類: System.Reflection.Emit.AssemblyBuilder:用來動態創建程序集 System.Reflection.Emit.ModuleBuilder:用來動態創建模塊 System.Reflection.Emit.TypeBuilder:用來動態創建類型 System.Reflection.Emit.MethodBuilder:用來動態創建方法 這里我的設計思想是,首先創建一個抽象基類(Creator類),它聲明了一個用于動態創建需要的對象實例的抽象方法,在運行時根據需要動態的創建出這個抽象類的子類,并動態實現這個抽象方法,編寫出用于創建對象的代碼。在基類中提供一些靜態方法來實現子類的創建過程,并對外提供可調用的方法。這是抽象工廠模式的一種實現。基類的聲明如下: public?abstract?class?Creator
...{
????????
public?abstract?Object?CreateObject(Object[]?param);
????????
private?staticvoid?CreateMethod(TypeBuilder?tb,?Type?originalType,?Object[]?param);
????????
public?static?Object?New(Type?type,?params?Object[]?param)?
}

抽象方法CreateObject就是用來在子類中重寫并創建實例的,靜態方法CreateMethod用于實現動態代碼生成的過程,靜態方法New就是對象暴露的方法,使用者通過這個方法來創建需要的實例,從而模擬new操作符,它的兩個參數分別代碼要創建的變量的類對象、構造函數的參數,這里使用了關鍵字params來修釋,也就是說它成為一個參數個數可變的函數,可以適應各種參數類型的構造函數。 New方法里面首先要動態的創建程序集和模塊: AssemblyBuilder?dynamicAssembly?=?AppDomain.CurrentDomain.DefineDynamicAssembly(new?AssemblyName("DynamicAssembly"),?AssemblyBuilderAccess.Run);
ModuleBuilder?moduleBuilder?
=?dynamicAssembly.DefineDynamicModule("MainModule"); 參數AssemblyBuilderAccess.Run表示這個動態創建的程序集只用于執行,而不需要保存。有了程序集和模塊之后就需要創建Creator類的子類了,也就是工廠類: TypeBuilder?tb?=?moduleBuilder.DefineType("__dynamicCreator."?+?type.FullName,?TypeAttributes.Public?|?TypeAttributes.Class,?typeof(Creator));
CreateMethod(tb,?type,?param);
Creator?creator?
=?(Creator)Activator.CreateInstance(tb.CreateType());
return?creator.CreateObject(param); 這里動態工廠類的類名與要創建的對象的類名相同,名空間前面加上了“__dynamicCreator.”以示區別,參數typeof(Creator)表示這個類要從Creator類繼承。然后調用CreateMethod方法來完成動態代碼生成,然后調用TypeBuilder的CreateType方法,它會根據之前動態創建的代碼生成一個新的類,并在之后可以立即使用,然后我使用Activator.CreateInstance創建出工廠類的實例,之后就可以通過調用這個實例的CreateObject方法來創建出需要的對象了。需要說明的是這里的代碼只是一個示例,真正要使用時還需要對創建出的creator對象進行緩存,以后再次創建相同類型的對象時就可以直接使用它的creator對象了。CreateMethod方法是最核心的地方,它需要根據我們指定的類對象和參數找到適當的構造函數,動態為工廠類創建CreateObject方法,在其中調用找到的構造函數,返回構造出的對象。首先得到基類中抽象方法CreateObject的信息: MethodInfo?mi?=?typeof(Creator).GetMethod("CreateObject"); 然后根據這個方法信息創建出子類的同名方法: MethodBuilder?mb?=?tb.DefineMethod("CreateObject",?mi.Attributes?&?~MethodAttributes.Abstract,?mi.CallingConvention,?mi.ReturnType,?new?Type[]?...{?typeof(Object[])?}); 注意這里指定方法屬性時需要去除掉基類方法的抽象屬性,否則在創建實例時會失敗,其他地方都完全和基類方法一樣。下面要在被創建對象的類型中查找適當的構造函數。查找的方法是針對每一個構造函數,檢查它的參數個數和參數類型與所傳入的參數信息是否相容,如果找不到完全相容的構造函數,那么說明用戶傳入的參數有誤,需要拋出異常: ConstructorInfo[]?cis?=?originalType.GetConstructors();?//反射出所有的構造函數
ConstructorInfo?theCi?=?null;
ParameterInfo[]?cpis?
=?null;
foreach(ConstructorInfo?ci?in?cis)
...{
????cpis?
=?ci.GetParameters();
????
if?(cpis.Length?!=?param.Length)?//參數個數不相符
????????continue;
????theCi?
=?ci;
????
for?(int?i?=?0;?i?<?cpis.Length;?i++)
????
...{
????????
if?(!(param[i]?==?null?||?param[i].GetType()?==?cpis[i].ParameterType?||?param[i].GetType().IsSubclassOf(cpis[i].ParameterType)))?????//參數類型不相符
????????...{
theCi?
=?null;
break;
????????}

????}

????
if?(theCi?!=?null)?//如果找到了完全相符的構造函數
????????break;
}

?
if?(theCi?==?null)
????
throw?new?ArgumentException("錯誤的參數個數或類型");
現在萬事具備,下面就要開始動態生成代碼了。要動態的生成可執行代碼需要用到ILGenerator類,使用MethodBuilder類的GetILGenerator方法即可以得到這個對象,然后調用它的Emit方法生成中間語言指令: ILGenerator?ilg?=?mb.GetILGenerator();
for?(int?i?=?0;?i?<?param.Length;?i++)
...{這里要循環處理傳入的每一個參數,以下通過IL來完成取數組元素并壓棧的操作: ????ilg.Emit(OpCodes.Ldarg_1);????????//把參數數組放入棧
????ilg.Emit(OpCodes.Ldc_I4,?i);??????//把下標壓入棧
ilg.Emit(OpCodes.Ldelem_Ref);????//以引用的方法從數組中取出需要的內容并放入棧
注意經過Ldelem_Ref指令以后,之前壓入棧的兩個參數會自動的彈出,而從數組中取得的內容會被放入棧中,所以只要反復的經過上述過程,就可以將傳入的參數逐一放入棧中。需要注意的是,這里只處理了引用類型,也就是說如果原來需要的參數是值類弄的,那么參數會在調用函數時被裝箱,這里需要還原到原來的值類型,也就是需要一個拆箱操作: ????if?(cpis[i].ParameterType.IsValueType)?????//判斷是否需要拆箱
????????ilg.Emit(OpCodes.Unbox_Any,?cpis[i].ParameterType);?//拆箱為需要的類型
} Unbox操作也會自動從棧中取出一個元素,拆箱后再把結果放回棧中,也就是說上述過程不會影響棧中元素的個數。經過上述過程,構造函數的參數就已經準備好并放入棧中了,下面就是調用構造函數了: ilg.Emit(OpCodes.Newobj,?theCi); 指令Newobj相當于關鍵字new,用于調用構函數,參數theCi就是要創建的對象的構造函數信息。經過這個過程原棧中所壓的構造函數參數都被彈出,然后把創建后的對象放回到棧中。下面只需要通過Ret指令就可以把棧中唯一的元素作為函數的返回值返回給調用者: ilg.Emit(OpCodes.Ret);? 到這里所需要的代碼就已經動態生成完畢,以后再通過Creator類的子類調用CreateObject方法時,執行的就是上述動態生成的代碼了。但只有這些還不夠,Creator子類的虛方法表還沒有更新,需要調用TypeBuilder類的DefineMethodOverride方法明確的指出用子類中的方法覆蓋基類中的虛方法: tb.DefineMethodOverride(mb,?mi);????//?定義方法重載 這段代碼雖然比直接使用反射技術要復雜很多,而且里面也多處使用了反射技術,但它只有在第一次使用時被調用到,之后就只調用動態生成的代碼,因此對性能影響是可以忽略的。 下面就是要使用上述代碼了。假如我們有一個類如下: public?class?MyClass
...{
????
public?MyClass(int?p1,?string?p2)?...{?}
}
而我們在編譯時并沒有此類的聲明,只有保存了類名稱的字符串和構造函數的參數類型,那么可以通過如下方法創建實例: String?className?=?"MyClass";
Type?t?
=?Type.GetType(className);
Object?o?
=?Creator.New(t,?1,?"haha"); 用起來還是比較方便的,至少不比反射麻煩。 為了進一步研究這種方法相對于反射方法的優勢,我進行了一組實驗。首先構造一個類,它有六個構造函數,分別用于測試一個、三個、九個值類型、引用類型參數時的性能: ????public?class?A
????
...{
????????
public?A(string?s,?string?s2,?string?s3,?string?s4,?string?s5,?string?s6,?string?s7,?string?s8,?string?s9)?...{?}
????????
public?A(string?s,?string?s2,?string?s3)?...{?}
????????
public?A(string?s)?...{?}
????????
public?A(int?a,?int?b,?int?c,?int?d,?int?e,?int?f,?int?g,?int?h,?int?i)?...{?}
????????
public?A(int?a,?int?b,?int?c)?...{?}
????????
public?A(int?a)?...{?}
????}
然后通過四種方式調用這六個構造函數來創建實例:Activator.CreateInstance、ConstructorInfo.Invoke、Creator.New、直接使用new,每種調用都重復1000萬次,在Intel PentiumM 1.86G、512M內存、Windows XP SP2、.Net Framewor 2.0上測試結果如下: 各種調用類型重復1000萬次所需時間(毫秒)
調用方式 Activator. CreateInstance ConstructorInfo. Invoke Creator.New 直接使用new
引用類型 1個參數 59281.25 18843.75 2296.875 140.625
3個參數 72031.25 24000 2453.125 171.875
9個參數 102843.75 39218.75 3187.5 156.25
值類型 1個參數 60468.75 19921.875 2375 109.375
3個參數 73953.125 26390.625 2796.875 109.375
9個參數 110656.25 46765.625 4453.125 109.375

可見,直接使用new還是最快的,動態代碼生成的方法還是要比直接使用new慢了15-40倍,但比使用Activator的方法快20倍左右,比Invoke的方法快10倍左右,因此在不能直接使用new的時候,動態代碼生成的方法還是非常實用的。

附完整的源代碼如下。此代碼仍有一些問題,如當一個類有多個構造函數時,它只能緩存一個構造函數,第二次如果調用另一個則會出錯,可以適當的改進來解決此問題。 ????public?abstract?class?Creator
????
...{
????????
private?static?AssemblyBuilder?dynamicAssembly?=?null;
????????
private?static?ModuleBuilder?moduleBuilder?=?null;
????????
private?static?Dictionary<Type,?Creator>?creatorList?=?new?Dictionary<Type,?Creator>();
????????
private?static?ModuleBuilder?GetDynamicModule()
????????
...{
????????????
if?(dynamicAssembly?==?null)
????????????
...{
????????????????dynamicAssembly?
=?AppDomain.CurrentDomain.DefineDynamicAssembly(new?AssemblyName("DynamicAssembly"),?AssemblyBuilderAccess.Run);
????????????????moduleBuilder?
=?dynamicAssembly.DefineDynamicModule("MainModule");
????????????}

?
????????????
return?moduleBuilder;
????????}

????????
????????
private?static?void?CreateMethod(TypeBuilder?tb,?Type?originalType,?Object[]?param)
????????
...{
????????????MethodInfo?mi?
=?typeof(Creator).GetMethod("CreateObject");
?
????????????MethodBuilder?mb?
=?tb.DefineMethod("CreateObject",?mi.Attributes?&?~MethodAttributes.Abstract,?mi.CallingConvention,?mi.ReturnType,?new?Type[]?...{?typeof(Object[])?});
?
????????????ConstructorInfo[]?cis?
=?originalType.GetConstructors();
????????????ConstructorInfo?theCi?
=?null;
????????????ParameterInfo[]?cpis?
=?null;
????????????
foreach(ConstructorInfo?ci?in?cis)
????????????
...{
????????????????cpis?
=?ci.GetParameters();
????????????????
if?(cpis.Length?!=?param.Length)
????????????????????
continue;
?
????????????????theCi?
=?ci;
????????????????
for?(int?i?=?0;?i?<?cpis.Length;?i++)
????????????????
...{
????????????????????
if?(!(param[i]?==?null?||?param[i].GetType()?==?cpis[i].ParameterType?||?param[i].GetType().IsSubclassOf(cpis[i].ParameterType)))
????????????????????
...{
????????????????????????theCi?
=?null;
????????????????????????
break;
????????????????????}

????????????????}

????????????????
if?(theCi?!=?null)
????????????????????
break;
????????????}

?
????????????
if?(theCi?==?null)
????????????????
throw?new?ArgumentException("錯誤的參數個數或類型");
?
????????????ILGenerator?ilg?
=?mb.GetILGenerator();
????????????
for?(int?i?=?0;?i?<?param.Length;?i++)
????????????
...{
????????????????ilg.Emit(OpCodes.Ldarg_1);
????????????????ilg.Emit(OpCodes.Ldc_I4,?i);
????????????????ilg.Emit(OpCodes.Ldelem_Ref);
????????????????
if?(cpis[i].ParameterType.IsValueType)
????????????????????ilg.Emit(OpCodes.Unbox_Any,?cpis[i].ParameterType);
????????????}

????????????ilg.Emit(OpCodes.Newobj,?theCi);
????????????ilg.Emit(OpCodes.Ret);
?
????????????tb.DefineMethodOverride(mb,?mi);????
//?定義方法重載
????????}

?
????????
private?static?Creator?GetCreator(Type?type,?Object[]?param)
????????
...{
????????????
if(!creatorList.ContainsKey(type))
????????????
...{
????????????????ModuleBuilder?module?
=?GetDynamicModule();
????????????????TypeBuilder?tb?
=?module.DefineType("__dynamicCreator."?+?type.FullName,?TypeAttributes.Public?|?TypeAttributes.Class,?typeof(Creator));
????????????????CreateMethod(tb,?type,?param);
????????????????creatorList.Add(type,?(Creator)Activator.CreateInstance(tb.CreateType()));
????????????}

????????????
return?creatorList[type];
????????}

?
????????
public?abstract?Object?CreateObject(Object[]?param);
?
????????
public?static?Object?New(Type?type,?params?Object[]?param)
????????
...{
????????????Creator?creator?
=?GetCreator(type,?param);
????????????
return?creator.CreateObject(param);
????????}

?}

?

轉載于:https://www.cnblogs.com/springMVC/archive/2007/02/23/2204713.html

總結

以上是生活随笔為你收集整理的基于动态代码生成技术的动态对象工厂的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。