17-09-12, 12:56 PM
Reflection.Emit – - إنشاء الأنواع ديناميكيا وقت التشغيل واستدعاء أعضائها.
يمكِّن Reflection.Emit من إنشاء الأنواع الجديدة ديناميكيا وقت التشغيل بإمكانك إنشاء التجميعات وتعريف الوحدات Modules, والأنواع , والإجراءات والخصائص...التي ترغب بإضافتها إليها, يمكن تشغيل تلك التجميعات تلقائيا أو يمكنك حفظها إلى القرص, يتم استدعاء اعضاء الأنواع المعرفة أثناء وقت التشغيل دائما باستخدام الإجراء Type.InvokeMember.
يمكِّن Reflection.Emit أيضا المترجمات Compilers من أرسال بيانات التعريف MetaData, وبيانات لغة Microsoft الوسيطة MSIL أثناء التشغيل.
أريد أن أضيف أيضا أن لتقنية CodeDom علاقة بالموضوع ولو أجرينا مقارنة بين التقنيتين سيتضح ذلك ما يلي:
محاسن استخدام Reflection.Emit
مساوئ استخدامها:
محاسن استخدام CodeDom
مساوئ استخدام CodeDom
للمزيد من المعلومات حول CodeDom, راجع مقال الاستاذ BADRMEDIA: معلومة ::: أجعل المستخدم عبر تطبيقك يخرج برامج أخري - CodeDom
سنقوم في المثال التالي بإنشاء فئة DoMath وتعريف إجراء DoSum بداخل التجميع المسمى Math أول ما يجب علينا القيام به, هو تعريف كائن من النوع AssemblyName وإعطائه اسما ما.
كود VB
كود C#
بعد ذلك سنستخدم الفئة AssemblyBuilder لتعريف تجميع ديناميكي في المجال الحالي للتطبيق AppDomain.
يجب علينا تمرير معاملين الأول هو كائن AssemblyName المعرّف سابقاً والثاني ترقيم من النوع AssemblyBuilderAccess وهو يأخذ أحد القيم الثلاث إما Run, RunAndSave, أو Save, والذي يحدد إن كان سيتم تشغيل التجميع فقط, أو حفظه إلى القرص.
كود VB
كود C#
والآن لإنشاء Module داخل التجميع السابق, سنقوم بتنفيذ احد أعضاء الفئة AssemblyBuilder وهو DefineDynamicModule والذي يعيد كائنا من النوع ModuleBuilder, يجب تمرير معاملين للإجراء السابق, اسم Module, واسم الملف الذي سيحفظ به.
كود VB
كود C#
الخطوة التالية هي إنشاء فئة داخل ال Module الذي انشاناه للتو, وسنسميها DoMath.
للقيام بذلك سنستخدم الفئة TypeBuilder لتعريف الفئة الجديدة سنستدعي الإجراء AssemblyModule.DefineType والتي تعيد كائن من النوع TypeBuilder
كود VB
كود C#
بعد ذلك سنقوم بكتابة الإجراء Sum للقيام بجمع عددين وإرجاع الناتج, للقيام بذلك سنستعين بالفئة MethodBuilder التي تحتوي على إجراء DefineMethod’ يأخذ هذا الإجراء القيم التالية: اسم الإجراء (String), سمات الإجراء الجديد MethodAttributes Enum, نوع القيمة العائدة, مصفوفة أنواع الممررات Parameters.
كود VB
كود C#
العملية السابقة غير كافية, يجب تعريف ممررين جديدين باستخدام الإجراء DefineParameter للفئة MethodBuilder, الكائن العائدة من هذا الإجراء هو من النوع ParameterBuilder , نمرر لهذا الإجراء فهرس الممرر Parameter Index, وقيمة الترقيم ParameterAttribute, ثم اسم الممرر.
كود VB
كود C#
الخطوة القادمة هي الأهم, وهي كتابة تعليمات MSIL عن طريق الكود, نخيل وكأن برنامجك عبارة عن مترجم Compiler, سنستخدم لذلك الفئة ILGenerator العائدة من الإجراء MethodBuilder.GetILGenerator:
كود VB
كود C#
تقوم الفئة ILGenerator بإرسال Opcodes MSIL تماما كما يقوم مترجم Visual Basic, الفئة Opcodes تحتوي على حقول Fields كل حقل يعبر عن تعليمة من تعليمات MSIL, في الكود التالي سنقوم بتمرير Arguments الخاصة بالإجراء Sum بحيث ستقوم الفئة ILGenerator اولاُ بوضع الممررات في الرصة Stack, ثم بعد ذلك تقوم بتنفيذ التعليمة المناسبة إلى الرصة (في مثالنا هذا سنمرر التعليمة Add التي تمثل عملية الجمع), التعليمة التالية التي ستدخل الرصة هي التعليمة Ret والتي تقوم بإرجاع ناتج التعليمة Add, الكود التالي سيوضح لك:
كود VB
كود C#
الخطوة التالية, هي إنهاء عملية كتابة الفئة الجديدة DoMath وإرجاع التجميع الجديد Math
كود VB
كود C#
يمكن بسهولة حفظ التجميع الجديد على القرص
لاستخدام التجميع الذي أنشاناه ديناميكيا أثناء وقت التشغيل, يجب استخدام الفئة Activator
لاحظ انه من الصعب التعرف تلقائيا على الانواع الجديدة التي انشاناها سابقا لذلك الحل الوحيد لاستدعاء تلك الأنواع هو استخدام Reflection مجددا على التجميع الجديد ثم استخدام الإجراء InvokeMemeber لتنفيذ الإجراء الجديد Sum الخاصة بالفئة الجديدة DoMath
كود VB
كود C#
انتهى الموضوع, لتحميل المثال اضغظ هنا
المراجع المعتمدة:
MSDN
The Code Project
WikiPedia
يمكِّن Reflection.Emit من إنشاء الأنواع الجديدة ديناميكيا وقت التشغيل بإمكانك إنشاء التجميعات وتعريف الوحدات Modules, والأنواع , والإجراءات والخصائص...التي ترغب بإضافتها إليها, يمكن تشغيل تلك التجميعات تلقائيا أو يمكنك حفظها إلى القرص, يتم استدعاء اعضاء الأنواع المعرفة أثناء وقت التشغيل دائما باستخدام الإجراء Type.InvokeMember.
يمكِّن Reflection.Emit أيضا المترجمات Compilers من أرسال بيانات التعريف MetaData, وبيانات لغة Microsoft الوسيطة MSIL أثناء التشغيل.
أريد أن أضيف أيضا أن لتقنية CodeDom علاقة بالموضوع ولو أجرينا مقارنة بين التقنيتين سيتضح ذلك ما يلي:
محاسن استخدام Reflection.Emit
- يمكن إنشاء تجميعات ديناميكية والعمل عليها بشكل مستقل, مما يمكِّن من تقليل حجم الذاكرة المستخدمة.
- يمكن توليد فئات حقيقية وإنشاء اعضاء داخلية (الاعضاء الديناميكية)
- إمكانيات الكود الناتج مستقل عن اللغة التي تكتب بها, لأنه يعتمد في الأساس على توليد تعليمات IL.
- أسرع وأحسن بكثير من ناحية الأداء.
مساوئ استخدامها:
- ليست قابلة للتنقيح Debugging, لذلك من الصعب اكتشاف الأخطاء بها.
- احتمال وجود أخطاء في الفئات الديناميكية كبير.
محاسن استخدام CodeDom
- سهل التعامل والاستخدام
- الكود الذي يتم توليده قابل للتنقيح Debugging
- يمكن اكتشاف الأخطاء وإصلاحها بسهولة
مساوئ استخدام CodeDom
- لايمكن إنشاء فئات جديدة في أي وقت, وبشكل مستقل.
- إمكانيات الكود الناتج مرتبط بإمكانيات اللغة التي تكتب بها الكود.
- أبطئ في الأداء من Reflection.Emit
للمزيد من المعلومات حول CodeDom, راجع مقال الاستاذ BADRMEDIA: معلومة ::: أجعل المستخدم عبر تطبيقك يخرج برامج أخري - CodeDom
سنقوم في المثال التالي بإنشاء فئة DoMath وتعريف إجراء DoSum بداخل التجميع المسمى Math أول ما يجب علينا القيام به, هو تعريف كائن من النوع AssemblyName وإعطائه اسما ما.
كود VB
كود :
Dim assemblyName As New AssemblyName()
assemblyName.Name = "Math"كود C#
كود :
AssemblyName assemblyName = new AssemblyName();
assemblyName.Name = "Math";بعد ذلك سنستخدم الفئة AssemblyBuilder لتعريف تجميع ديناميكي في المجال الحالي للتطبيق AppDomain.
يجب علينا تمرير معاملين الأول هو كائن AssemblyName المعرّف سابقاً والثاني ترقيم من النوع AssemblyBuilderAccess وهو يأخذ أحد القيم الثلاث إما Run, RunAndSave, أو Save, والذي يحدد إن كان سيتم تشغيل التجميع فقط, أو حفظه إلى القرص.
كود VB
كود :
Dim CreatedAssembly As AssemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave)كود C#
كود :
AssemblyBuilder CreatedAssembly =
AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);والآن لإنشاء Module داخل التجميع السابق, سنقوم بتنفيذ احد أعضاء الفئة AssemblyBuilder وهو DefineDynamicModule والذي يعيد كائنا من النوع ModuleBuilder, يجب تمرير معاملين للإجراء السابق, اسم Module, واسم الملف الذي سيحفظ به.
كود VB
كود :
Dim AssemblyModule As ModuleBuilder = CreatedAssembly.DefineDynamicModule("MathModule", "Math.dll")كود C#
كود :
ModuleBuilder AssemblyModule =
CreatedAssembly.DefineDynamicModule("MathModule","Math.dll");الخطوة التالية هي إنشاء فئة داخل ال Module الذي انشاناه للتو, وسنسميها DoMath.
للقيام بذلك سنستخدم الفئة TypeBuilder لتعريف الفئة الجديدة سنستدعي الإجراء AssemblyModule.DefineType والتي تعيد كائن من النوع TypeBuilder
كود VB
كود :
Dim MathType As TypeBuilder = AssemblyModule.DefineType("DoMath", TypeAttributes. Or TypeAttributes.)كود :
TypeBuilder MathType = AssemblyModule.DefineType("DoMath",
TypeAttributes.Public | TypeAttributes.Class);بعد ذلك سنقوم بكتابة الإجراء Sum للقيام بجمع عددين وإرجاع الناتج, للقيام بذلك سنستعين بالفئة MethodBuilder التي تحتوي على إجراء DefineMethod’ يأخذ هذا الإجراء القيم التالية: اسم الإجراء (String), سمات الإجراء الجديد MethodAttributes Enum, نوع القيمة العائدة, مصفوفة أنواع الممررات Parameters.
كود VB
كود :
Dim ParamTypes As System.Type() = New Type() {GetType(Integer), GetType(Integer)}
Dim SumMethod As MethodBuilder = MathType.DefineMethod("Sum", MethodAttributes.Public, GetType(Integer), ParamTypes)كود C#
كود :
System.Type [] ParamTypes = new Type[] { typeof(int),typeof(int) };
MethodBuilder SumMethod = MathType.DefineMethod("Sum",
MethodAttributes.Public, typeof(int), ParamTypes);العملية السابقة غير كافية, يجب تعريف ممررين جديدين باستخدام الإجراء DefineParameter للفئة MethodBuilder, الكائن العائدة من هذا الإجراء هو من النوع ParameterBuilder , نمرر لهذا الإجراء فهرس الممرر Parameter Index, وقيمة الترقيم ParameterAttribute, ثم اسم الممرر.
كود VB
كود :
Dim Param1 As ParameterBuilder = SumMethod.DefineParameter(1, ParameterAttributes.In, "num1")
Dim Param2 As ParameterBuilder = SumMethod.DefineParameter(2, ParameterAttributes.In, "num2")كود C#
كود :
ParameterBuilder Param1 =
SumMethod.DefineParameter(1,ParameterAttributes.In ,"num1");
ParameterBuilder Param2 =
SumMethod.DefineParameter(2,ParameterAttributes.In ,"num2");الخطوة القادمة هي الأهم, وهي كتابة تعليمات MSIL عن طريق الكود, نخيل وكأن برنامجك عبارة عن مترجم Compiler, سنستخدم لذلك الفئة ILGenerator العائدة من الإجراء MethodBuilder.GetILGenerator:
كود VB
كود :
Dim ilGenerator As ILGenerator = SumMethod.GetILGenerator()كود :
ILGenerator ilGenerator = SumMethod.GetILGenerator();كود VB
كود :
ilGenerator.Emit(OpCodes.Ldarg_1)
ilGenerator.Emit(OpCodes.Ldarg_2)
ilGenerator.Emit(OpCodes.Add)
ilGenerator.Emit(OpCodes.Ret)كود C#
كود :
ilGenerator.Emit(OpCodes.Ldarg_1);
ilGenerator.Emit (OpCodes.Ldarg_2);
ilGenerator.Emit (OpCodes.Add );
ilGenerator.Emit(OpCodes.Ret);الخطوة التالية, هي إنهاء عملية كتابة الفئة الجديدة DoMath وإرجاع التجميع الجديد Math
كود VB
كود :
MathType.CreateType()
Return CreatedAssemblyكود C#
كود :
MathType.CreateType();
return CreatedAssembly;يمكن بسهولة حفظ التجميع الجديد على القرص
كود :
CreatedAssembly.Save("Math.dll")لاستخدام التجميع الذي أنشاناه ديناميكيا أثناء وقت التشغيل, يجب استخدام الفئة Activator
لاحظ انه من الصعب التعرف تلقائيا على الانواع الجديدة التي انشاناها سابقا لذلك الحل الوحيد لاستدعاء تلك الأنواع هو استخدام Reflection مجددا على التجميع الجديد ثم استخدام الإجراء InvokeMemeber لتنفيذ الإجراء الجديد Sum الخاصة بالفئة الجديدة DoMath
كود VB
كود :
Dim Parameters As Object() = New Object(1) {}
Parameters(0) = DirectCast((5), Object)
Parameters(1) = DirectCast((9), Object)
Dim EmitObj As Object = Activator.CreateInstance(MathType, False)
Dim Result As Object = MathType.InvokeMember("Sum", BindingFlags.InvokeMethod, Nothing, EmitObj, Parameters)
Console.WriteLine("Sum of {0}+{1} is {2}", Parameters(0), Parameters(1), Result.ToString())كود C#
كود :
object[] Parameters = new object [2];
Parameters[0] = (object) (5);
Parameters[1] = (object) (9);
object EmitObj = Activator.CreateInstance(MathType,false);
object Result = MathType.InvokeMember("Sum",
BindingFlags.InvokeMethod ,null,EmitObj,Parameters);
Console.WriteLine ("Sum of {0}+{1} is {2}",
Parameters[0],Parameters[1],Result.ToString ());انتهى الموضوع, لتحميل المثال اضغظ هنا
المراجع المعتمدة:
MSDN
The Code Project
WikiPedia

