ایجاد کلاس پویا (Dynamic) در زمان اجرا در #C

در این مقاله نحوه ایجاد کلاس موجود در نرم افزار در زمان اجرا را همراه با ذکر مثال شرح خواهیم داد

معمولاً برنامه نویسان در زمان طراحی نرم افزار با استفاده از کلمات کلیدی کلاسهای متعددی را تولید می کنند .آیا شما تا تجربه ایجاد کلاس در زمان اجرا(Run Time) را داشته اید ؟در زیر ما مراحل این عملیات را توضیح خواهیم داد .در زیر به تعدادی از این کلاسها که در اکثر نرم افزارها از آنها استفاده می کنیم ذکر شده است :

AssemblyName

AssemblyBuilder 

ModuleBuilder 

TypeBuilder

FieldBuilder

PropertyBuilder

اکنون هر یک از کلاسهای ذکر شده را شرح می دهیم .

AssemblyName

این کلاس در فضای نام System.Reflection موجود میباشد ، که یک نمونه از کلاس System.Reflection.AssemblyName با نام اختصاصی ایجاد میکند .به مثال زیر توجه نمایید :


    AssemblyName asemblyName;  
    this.asemblyName = new AssemblyName(ClassName);   

در کد بالا از کلاس اشاره شده می توانیم با نام دلخواه یک کلاس ایجاد کنیم .

AssemblyBuilder

این کلاس به ما در ایجاد assembly در زمان اجرا کمک می کند ، همچنین این کلاس شامل هیچ constructor نمی باشد .


    AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(this.asemblyName, AssemblyBuilderAccess.Run);

ModuleBuilder

این کلاس برای ایجاد یک ماژول در پروژه استفاده میشود .ار کلاس Module ارث بری میکند و از فضای نام System.Reflection.Emit و رابط ModuleBuilder را اجرا می کند .این کلاس نیز هیج constructor ندارد ، از این رو شئ های موجود در کلاس می توانند در تابع DefineDynamicModule ایجاد شوند .


    ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");  

TypeBuilder

این کلاس نیز یک نمونه از کلاسی بخصوص را ایجاد میکند که از System.Reflection.TypeInfo ارث بری میکند .از فضای نام System.Reflection.Emit استفاده میکند .


    TypeBuilder typeBuilder = moduleBuilder.DefineType(this.asemblyName.FullName  
                                  , TypeAttributes.Public |  
                                  TypeAttributes.Class |  
                                  TypeAttributes.AutoClass |  
                                  TypeAttributes.AnsiClass |  
                                  TypeAttributes.BeforeFieldInit |  
                                  TypeAttributes.AutoLayout  
                                  , null);  

FieldBuilder

این کلاس از System.Reflection.FieldInfo ارث بری می کند


    FieldBuilder fieldBuilder = typeBuilder.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);  

PropertyBuilder 

این کلاس خاصیت یک کلاس در زمان اجرا را فعال میکند و در فضای نام System.Reflection.Emit قرار دارد و از System.Reflection.PropertyInfo ارث بری میکند .


    PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);  

در مثال موجود در این مقاله از Console Application استفاده می کنیم که شامل کلاسهای زیر میباشد :

Create assembly

Create Module

Create Instance

Create constructor 

Create properties

Create assembly 

قبل از ایجاد یک نمونه از کلاس ما باید یک assembly تعریف کنیم :


    AssemblyName asemblyName;  
    this.asemblyName = new AssemblyName(ClassName);  
    AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(this.asemblyName, AssemblyBuilderAccess.Run);  

Create Module

با استفاده از کلاس module builder این کلاس یک رشته به عنوان module name ایجاد می کند .


    ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");  

Create Instance

کلاس TypeBuilder نمونه ای از کلاس را در شئی ایجاد می کند .


    TypeBuilder typeBuilder = moduleBuilder.DefineType(this.asemblyName.FullName  
                                  , TypeAttributes.Public |  
                                  TypeAttributes.Class |  
                                  TypeAttributes.AutoClass |  
                                  TypeAttributes.AnsiClass |  
                                  TypeAttributes.BeforeFieldInit |  
                                  TypeAttributes.AutoLayout  
                                  , null);  

Create constructor

بعد از ایجاد کلاس ، constructor مربوط به آنها را با استفاده از تابع DefineDefaultContructor ایجاد میکند .

typeBuilder.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);  

Create properties

عملیات ایجاد خاصیت نسبت به دیگر کلاسها مقداری طولانی تر می باشد . برای ایجاد این کلاس ما باید یک field تولید کنیم .


    FieldBuilder fieldBuilder = typeBuilder.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);  
      
              PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);  
              MethodBuilder getPropMthdBldr = typeBuilder.DefineMethod("get_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, propertyType, Type.EmptyTypes);  
              ILGenerator getIl = getPropMthdBldr.GetILGenerator();  
      
              getIl.Emit(OpCodes.Ldarg_0);  
              getIl.Emit(OpCodes.Ldfld, fieldBuilder);  
              getIl.Emit(OpCodes.Ret);  
      
              MethodBuilder setPropMthdBldr =typeBuilder.DefineMethod("set_" + propertyName,  
                    MethodAttributes.Public |  
                    MethodAttributes.SpecialName |  
                    MethodAttributes.HideBySig,  
                    null, new[] { propertyType });  
      
              ILGenerator setIl = setPropMthdBldr.GetILGenerator();  
              Label modifyProperty = setIl.DefineLabel();  
              Label exitSet = setIl.DefineLabel();  
      
              setIl.MarkLabel(modifyProperty);  
              setIl.Emit(OpCodes.Ldarg_0);  
              setIl.Emit(OpCodes.Ldarg_1);  
              setIl.Emit(OpCodes.Stfld, fieldBuilder);  
      
              setIl.Emit(OpCodes.Nop);  
              setIl.MarkLabel(exitSet);  
              setIl.Emit(OpCodes.Ret);  
      
              propertyBuilder.SetGetMethod(getPropMthdBldr);  
              propertyBuilder.SetSetMethod(setPropMthdBldr);  

لیست کامل کلاسها برای ایجاد کلاس در زمان اجرا به صورت زیر می باشد :


    using System;  
    using System.Collections.Generic;  
    using System.Linq;  
    using System.Reflection;  
    using System.Reflection.Emit;  
    using System.Text;  
    using System.Threading.Tasks;  
      
    namespace PropertyBuilderExample  
    {  
       public class MyClassBuilder  
        {  
           AssemblyName asemblyName;  
           public MyClassBuilder(string ClassName)  
           {  
               this.asemblyName = new AssemblyName(ClassName);  
           }  
           public object CreateObject(string[] PropertyNames,Type[]Types)  
           {  
               if(PropertyNames.Length!=Types.Length)  
               {  
                  Console.WriteLine("The number of property names should match their corresopnding types number");  
               }  
      
                 TypeBuilder DynamicClass = this.CreateClass();  
                 this.CreateConstructor(DynamicClass);  
                for(int ind=0;ind<PropertyNames.Count();ind++)  
                   CreateProperty(DynamicClass, PropertyNames[ind],Types[ind]);  
                Type type = DynamicClass.CreateType();  
      
               return Activator.CreateInstance(type);  
           }  
           private TypeBuilder CreateClass()  
           {  
               AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(this.asemblyName, AssemblyBuilderAccess.Run);  
               ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");  
               TypeBuilder typeBuilder = moduleBuilder.DefineType(this.asemblyName.FullName  
                                   , TypeAttributes.Public |  
                                   TypeAttributes.Class |  
                                   TypeAttributes.AutoClass |  
                                   TypeAttributes.AnsiClass |  
                                   TypeAttributes.BeforeFieldInit |  
                                   TypeAttributes.AutoLayout  
                                   , null);  
               return typeBuilder;  
           }  
           private void CreateConstructor(TypeBuilder typeBuilder)  
           {  
               typeBuilder.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);  
           }  
           private void CreateProperty(TypeBuilder typeBuilder, string propertyName, Type propertyType)  
           {  
               FieldBuilder fieldBuilder = typeBuilder.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);  
      
               PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);  
               MethodBuilder getPropMthdBldr = typeBuilder.DefineMethod("get_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, propertyType, Type.EmptyTypes);  
               ILGenerator getIl = getPropMthdBldr.GetILGenerator();  
      
               getIl.Emit(OpCodes.Ldarg_0);  
               getIl.Emit(OpCodes.Ldfld, fieldBuilder);  
               getIl.Emit(OpCodes.Ret);  
      
               MethodBuilder setPropMthdBldr =typeBuilder.DefineMethod("set_" + propertyName,  
                     MethodAttributes.Public |  
                     MethodAttributes.SpecialName |  
                     MethodAttributes.HideBySig,  
                     null, new[] { propertyType });  
      
               ILGenerator setIl = setPropMthdBldr.GetILGenerator();  
               Label modifyProperty = setIl.DefineLabel();  
               Label exitSet = setIl.DefineLabel();  
      
               setIl.MarkLabel(modifyProperty);  
               setIl.Emit(OpCodes.Ldarg_0);  
               setIl.Emit(OpCodes.Ldarg_1);  
               setIl.Emit(OpCodes.Stfld, fieldBuilder);  
      
               setIl.Emit(OpCodes.Nop);  
               setIl.MarkLabel(exitSet);  
               setIl.Emit(OpCodes.Ret);  
      
               propertyBuilder.SetGetMethod(getPropMthdBldr);  
               propertyBuilder.SetSetMethod(setPropMthdBldr);  
           }  
        }  
    }  

The following is the code for displaying the class details that were created at run time.

    using System;  
    using System.Collections.Generic;  
    using System.Linq;  
    using System.Reflection;  
    using System.Reflection.Emit;  
    using System.Text;  
    using System.Threading.Tasks;  
    using PropertyBuilderExample;  
      
    namespace PropertyBuilderExample  
    {  
        class Program  
        {  
            static void Main(string[] args)  
            {  
                MyClassBuilder MCB=new MyClassBuilder("Student");  
                var myclass = MCB.CreateObject(new string[3] { "ID", "Name", "Address" }, new Type[3] { typeof(int), typeof(string), typeof(string) });  
               Type TP = myclass.GetType();  
                  
                foreach (PropertyInfo PI in TP.GetProperties())  
                {  
                    Console.WriteLine(PI.Name);  
                }  
                Console.ReadLine();  
            }  
        }  
    }   

 

فایل های ضمیمه