مقال: Reflection وكيفية استخدامها في برامجنا - نسخة قابلة للطباعة +- منتدى فيجوال بيسك لكل العرب | منتدى المبرمجين العرب (http://vb4arb.com/vb) +-- قسم : الاقسام الاخرى (http://vb4arb.com/vb/forumdisplay.php?fid=74) +--- قسم : قسم المقالات العام (http://vb4arb.com/vb/forumdisplay.php?fid=85) +--- الموضوع : مقال: Reflection وكيفية استخدامها في برامجنا (/showthread.php?tid=4138) |
مقال: Reflection وكيفية استخدامها في برامجنا - Islam Ibrahim - 17-09-12 [ملحوظة: هذا الموضوع يناقش أغراض تعليمية فقط, ولا يمت بصلة بموضوع الهندسة العكسية.] Reflection من التقنيات الهامة للدوت نت, وعن طريقها يستطيع أي برنامج جمع معلومات التعريف MetaData الخاصة به والتعامل معها, وتعتبر آلية قوية وفعالة للتعامل مع الكائنات وقت التشغيل, أدوات ال Reflection توجد ضمن مساحة الإسم System.Reflection, تتيح الفئات والواجهات الموجودة ضمن مساحة الاسم System.Reflection المبرمجين من مراقبة وجمع المعلومات حول نوع Type معيّن, وكذلك الوصول إلى الخصائص والدوال والإجراءات الخاصة بذلك النوع باستخدام الإجراء Invoke, يمكن من خلالها أيضا كتابة تطبيقات و برامج الهندسة العكسية, مستعرضات للفئات Class Viewers, أو محررات الخصائص Property Editors, سنناقش في هذا الموضوع الاستخدامات التالية لآلية Reflection, وهي:
بعض المفاهيم الأساسية: System.Type: هذه الفئة أساسية ويمكن ان تمثل عدة أنواع, إما: الفئات, الواجهات المصفوفات, أوالتراقيم (هذه هي الأنواع التي تهمنا). .NET Managed Module (وحدة نمطية مُدارة): هو إما ملف تنفيذي محمول Portable Executable أو ملف نوع Type DLL, قد تحتوي على فئة واحدة أو واجهة واحدة أو اكثر, يمكن ان تحتوي هذه الوحدة النمطية على اكثر من مساحة اسم NameSpace واحدة, ويمكن أن تشمل مساحة اسم واحدة على أكثر من وحدة نمطية. واحدة او أكثر من الوحدات النمطية تشكل مجتمعة مايعرف بالـتجميع Assembly, لا يجب الخاط بين Module أو (وحدة التعليمات البرمجية) الخاصة بـ Visual Basic وبين الوحدة النمطية. يمكن الحصول على وحدة نمطية بعد ترجمة التعليمات البرمجية باستخدام Visual Studio أو باستخدام اداة مترجم اللغة vbc.exe (Visual Basic compiler) او الأداة csc.exe ( CSharp compiler) والموجودة ضمن ادوات سطر الأوامر Visual Studio Command Line tools. يمكن ربط أكثر من وحدة نمطية واحدة لتشكيل تجميع واحد باستخدام الأداة Al.exe (Assembly Linker), والمخطط التالي يوضح ذلك: تتشكل كل وحدة نمطية أساساً من الأجزاء التالية:
Application Domain (مجال التطبيق): هو البيئة التي يعمل عليها التطبيق حيث توفر تلك البيئة عزلاً لما يقوم به التطبيق عن باقي التطبيقات, تمكِّن الفئة AppDomain الوصول إلى مجال التطبيق الحالي, وتطبيق جملة من العمليات عليه. مقال: Reflection وكيفية استخدامها في برامجنا - Islam Ibrahim - 17-09-12 الكشف عن التجميعات المرتبطة (المحملة) مع أحد التطبيق أنشئ مشروع Console Application واستبدل محتويات Module1.vb بالكود التالي كود C# كود : using System; كود VB كود : Imports System.Reflection يقوم الإجراءا GetAssemblies() أحد اعضاء الفئة AppDomain بإرجاع التجميعات التي قام البرنامج ReflectionDemo بتحميلها والناتج هو التجميعين ReflectionDemo و MsCorLib عند تنفيذ البرنامج سيظهر في ال Console مايلي: كود : mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ReflectionDemo, Version=1.0.1882.29904, Culture=neutral, PublicKeyToken الحصول على الأنواع الموجودة ضمن تجميع معيّن أول ما يجب القيام به, هو تحميل التجميع ديناميكيا باستخدام الإجراء Assembly.Load(). كود VB كود : Dim LoadedAssembly As Assembly = Assembly.Load("mscorlib.dll") كود : Assembly LoadedAssembly = Assembly.Load("mscorlib.dll"); بعد تحميل التجميع, يمكن الإجراء GetTypes() للحصول على مصفوفة من النوع Type كود VB كود : Dim ExistingTypes As System.Type() = LoadedAssembly.GetTypes() كود C# كود : System.Type[] ExistingTypes = LoadedAssembly.GetTypes (); الأنواع العائدة يمكن أن تمثل إما فئات Classes, أو واجهات Interfaces, أو تراقيم Enumerations. كود VB كود : Imports System كود C# كود : using System; سيقوم الـ Console بعرض جميع الأنواع الموجودة ضمن التجميع mscorlib.dll, وهذا مجرد مقطع من الناتج: كود : System.Object تطبيق Reflection على نوع محدّد في المثال التالي, سنقوم بعملية انعكاس على نوع واحد والكشف عن الأعضاء الخاصة به, الإجراء Type.GetType تأخذ معامل واحد من النوع string, وتعيد كائن من النوع الموافق, وللاستعلام عن أعضاء نوع معين نستخدم الإجراء Type.GetMembers والتي تعيد مصفوفة من النوع MemberInfo كود VB: كود : Imports System كود C# كود : using System; الناتج سيكون كالتالي: كود : Members of System.Int32 الحصول على الأعضاء حسب أنواعها: كود : Dim Members As System.Reflection.MemberInfo() = TypetoReflect.GetMembers() كود : Dim Members As System.Reflection.MethodInfo() = TypetoReflect.GetMethods() إرجاع الإجراءات الخاصة بالنوع الذي نستعلم عنه. كود : Dim Members As System.Reflection.FieldInfo() = TypetoReflect.GetFields() كود : Dim Members As System.Reflection.PropertiesInfo() = TypetoReflect.GetProperties() كود : Dim Members As System.Reflection.EventInfo() = TypetoReflect.GetEvents() كود : Dim Members As System.Reflection.ConstructorInfo() = TypetoReflect.GetConstructors() كود : Dim Members As System.Type () = TypetoReflect.GetInterfaces() مقال: Reflection وكيفية استخدامها في برامجنا - Islam Ibrahim - 17-09-12 الاستدعاء الديناميكي باستخدام Type.InvokeMember المثال التالي يوضح كيفية استدعاء إجراء ما بشكل ديناميكي باستخدام الإجراء Type.InvokeMember سنقوم باستدعاء الإجراء Equals الخاص بالفئة System.String والذي يقوم بمقارنة سلسلتين نصيتين, حيث يقوم تمرير البرنامج التالي تمرير المعاملين النصيّين. يقوم الإجراء Type.InvokeMember باستدعاء إجراء ما من خلال اسمه. معاملات الإجراء Type.InvokeMember كالتالي:
كود VB: كود : Imports System كود C# كود : using System; الناتج في هذه الحالة false. مقال: Reflection وكيفية استخدامها في برامجنا - Islam Ibrahim - 17-09-12 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 كود : Dim assemblyName As New AssemblyName() كود C# كود : AssemblyName assemblyName = new AssemblyName(); بعد ذلك سنستخدم الفئة AssemblyBuilder لتعريف تجميع ديناميكي في المجال الحالي للتطبيق AppDomain. يجب علينا تمرير معاملين الأول هو كائن AssemblyName المعرّف سابقاً والثاني ترقيم من النوع AssemblyBuilderAccess وهو يأخذ أحد القيم الثلاث إما Run, RunAndSave, أو Save, والذي يحدد إن كان سيتم تشغيل التجميع فقط, أو حفظه إلى القرص. كود VB كود : Dim CreatedAssembly As AssemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave) كود C# كود : AssemblyBuilder CreatedAssembly = والآن لإنشاء Module داخل التجميع السابق, سنقوم بتنفيذ احد أعضاء الفئة AssemblyBuilder وهو DefineDynamicModule والذي يعيد كائنا من النوع ModuleBuilder, يجب تمرير معاملين للإجراء السابق, اسم Module, واسم الملف الذي سيحفظ به. كود VB كود : Dim AssemblyModule As ModuleBuilder = CreatedAssembly.DefineDynamicModule("MathModule", "Math.dll") كود C# كود : ModuleBuilder AssemblyModule = الخطوة التالية هي إنشاء فئة داخل ال Module الذي انشاناه للتو, وسنسميها DoMath. للقيام بذلك سنستخدم الفئة TypeBuilder لتعريف الفئة الجديدة سنستدعي الإجراء AssemblyModule.DefineType والتي تعيد كائن من النوع TypeBuilder كود VB كود : Dim MathType As TypeBuilder = AssemblyModule.DefineType("DoMath", TypeAttributes. Or TypeAttributes.) كود : TypeBuilder MathType = AssemblyModule.DefineType("DoMath", بعد ذلك سنقوم بكتابة الإجراء Sum للقيام بجمع عددين وإرجاع الناتج, للقيام بذلك سنستعين بالفئة MethodBuilder التي تحتوي على إجراء DefineMethod’ يأخذ هذا الإجراء القيم التالية: اسم الإجراء (String), سمات الإجراء الجديد MethodAttributes Enum, نوع القيمة العائدة, مصفوفة أنواع الممررات Parameters. كود VB كود : Dim ParamTypes As System.Type() = New Type() {GetType(Integer), GetType(Integer)} كود C# كود : System.Type [] ParamTypes = new Type[] { typeof(int),typeof(int) }; العملية السابقة غير كافية, يجب تعريف ممررين جديدين باستخدام الإجراء DefineParameter للفئة MethodBuilder, الكائن العائدة من هذا الإجراء هو من النوع ParameterBuilder , نمرر لهذا الإجراء فهرس الممرر Parameter Index, وقيمة الترقيم ParameterAttribute, ثم اسم الممرر. كود VB كود : Dim Param1 As ParameterBuilder = SumMethod.DefineParameter(1, ParameterAttributes.In, "num1") كود C# كود : ParameterBuilder Param1 = الخطوة القادمة هي الأهم, وهي كتابة تعليمات MSIL عن طريق الكود, نخيل وكأن برنامجك عبارة عن مترجم Compiler, سنستخدم لذلك الفئة ILGenerator العائدة من الإجراء MethodBuilder.GetILGenerator: كود VB كود : Dim ilGenerator As ILGenerator = SumMethod.GetILGenerator() كود : ILGenerator ilGenerator = SumMethod.GetILGenerator(); كود VB كود : ilGenerator.Emit(OpCodes.Ldarg_1) كود C# كود : ilGenerator.Emit(OpCodes.Ldarg_1); الخطوة التالية, هي إنهاء عملية كتابة الفئة الجديدة DoMath وإرجاع التجميع الجديد Math كود VB كود : MathType.CreateType() كود C# كود : MathType.CreateType(); يمكن بسهولة حفظ التجميع الجديد على القرص كود : CreatedAssembly.Save("Math.dll") لاستخدام التجميع الذي أنشاناه ديناميكيا أثناء وقت التشغيل, يجب استخدام الفئة Activator لاحظ انه من الصعب التعرف تلقائيا على الانواع الجديدة التي انشاناها سابقا لذلك الحل الوحيد لاستدعاء تلك الأنواع هو استخدام Reflection مجددا على التجميع الجديد ثم استخدام الإجراء InvokeMemeber لتنفيذ الإجراء الجديد Sum الخاصة بالفئة الجديدة DoMath كود VB كود : Dim Parameters As Object() = New Object(1) {} كود C# كود : object[] Parameters = new object [2]; انتهى الموضوع, لتحميل المثال اضغظ هنا المراجع المعتمدة: MSDN The Code Project WikiPedia مقال: Reflection وكيفية استخدامها في برامجنا - Omar Mekkawy - 17-09-12 بارك الله فيك أخي موضوع غاية في الروعة تم التقييم :d RE: مقال: Reflection وكيفية استخدامها في برامجنا - Fantastico - 13-01-16 أقدم لك جزيل الشكر أخي على هذا الموضوع الثمين لدي سؤال : هل من طريقة أخرى أسهل لكتابة الشيفرات داخل الإجراء غير الطريقة ilGenerator.Emit ثانيا اذا كان في متناولك بعض المواضيع المكملة او الكتب العربية التي تتحدث عن المجمعات وفئات الانعكاس فلك جزيل الشكر لو تفضلت علي بها. انا مهتم بمعرفة طريقة تحويل النص الى Method |