Dynamic Types with Reflection.Emit, how to ?

L

Luis Arvayo

Hi,

In c#, I need to dynamically create types at runtime that will consist of
the following:


- inherits from a given interface

- will have a constructor with an int argument

- Will have an event handler (derived from the interface)

- Will have a method that will return the value of the property or
properties based on firing the event handler and the hooked to the event
will return the value.


The prototype is something like the following:


// Event args for the event handler
public class PropertyValueEventArgs : EventArgs
{
int _recordNo;
public int RecordNo
{
get {return _recordNo;}
}

string _propertyName;
public string PropertyName
{
get {return _propertyName;}
}

Type _propertyType;
public Type PropertyType
{
get {return _propertyType;}
}

object _value;
public object Value
{
get {return _value;}
set {_value = value;}
}

public PropertyValueEventArgs(int recordNo, string propertyName, Type
propertyType, object value)
{
_recordNo = recordNo;
_propertyName = propertyName;
_propertyType = propertyType;
_value = value;
}
}

// delegate for the event handler

public delegate void PropertyValueEventHandler(object sender,
PropertyValueEventArgs e);

// The interfase that must be implemented by the dynamic type
public interface IDynamicDataFields
{
event PropertyValueEventHandler GetPropertyValue;
}

// note: the above two are already defined in other assembly


The prototye class that must be created at runtime :


public class LayerFields : IDynamicDataFields
{
// a private field
int _recordNo;

// the event
public event PropertyValueEventHandler GetPropertyValue;

// the constructor
public LayerFields(int recordNo)
{
_recordNo = recordNo;
}


// return value for the property based on the event
object _getPropertyValue(string propertyName, Type propertyType)
{
if(GetPropertyValue == null) return null;

PropertyValueEventArgs e = new PropertyValueEventArgs(_recordNo,
propertyName, propertyType, null);
GetPropertyValue(this, e);
return e.Value;
}


// here the list of variable number of properties of type string, int,
bool, and so on.

public string StringProp
{
get {return (string)_getPropertyValue("StringProp", typeof(string));}
}

public int IntProp
{
get (return (int)_getPropertyValue("IntProp", typeof(int));}
}

... and so on
}


This is the opcode generated by ildasm.exe

********* constructor

..method public hidebysig specialname rtspecialname
instance void .ctor(int32 recordNo) cil managed
{
// Code size 14 (0xe)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ldarg.0
IL_0007: ldarg.1
IL_0008: stfld int32 MapSolver.DynamicClasses.LayerFields::_recordNo
IL_000d: ret
} // end of method LayerFields::.ctor


********** _getPropertyValue method


..method private hidebysig instance object
_getPropertyValue(string propertyName,
class [mscorlib]System.Type propertyType) cil
managed
{
// Code size 51 (0x33)
.maxstack 5
.locals ([0] class [MyAssembly]MyAssembly.PropertyValueEventArgs e,
[1] object CS$00000003$00000000)
IL_0000: ldarg.0
IL_0001: ldfld class
[MyAssembly]MyAssembly.PropertyValueEventHandler
MapSolver.DynamicClasses.LayerFields::GetPropertyValue
IL_0006: brtrue.s IL_000c
IL_0008: ldnull
IL_0009: stloc.1
IL_000a: br.s IL_0031
IL_000c: ldarg.0
IL_000d: ldfld int32 MapSolver.DynamicClasses.LayerFields::_recordNo
IL_0012: ldarg.1
IL_0013: ldarg.2
IL_0014: ldnull
IL_0015: newobj instance void
[MyAssembly]MyAssembly.PropertyValueEventArgs::.ctor(int32,

string,

class [mscorlib]System.Type,

object)
IL_001a: stloc.0
IL_001b: ldarg.0
IL_001c: ldfld class
[MyAssembly]MyAssembly.PropertyValueEventHandler
MapSolver.DynamicClasses.LayerFields::GetPropertyValue
IL_0021: ldarg.0
IL_0022: ldloc.0
IL_0023: callvirt instance void
[MyAssembly]MyAssembly.PropertyValueEventHandler::Invoke(object,

class [MyAssembly]MyAssembly.PropertyValueEventArgs)
IL_0028: ldloc.0
IL_0029: callvirt instance object
[MyAssembly]MyAssembly.PropertyValueEventArgs::get_Value()
IL_002e: stloc.1
IL_002f: br.s IL_0031
IL_0031: ldloc.1
IL_0032: ret
} // end of method LayerFields::_getPropertyValue


**************** return the property value


..method public hidebysig specialname instance string
get_StringProp() cil managed
{
// Code size 31 (0x1f)
.maxstack 3
.locals ([0] string CS$00000003$00000000)
IL_0000: ldarg.0
IL_0001: ldstr "StringProp"
IL_0006: ldtoken [mscorlib]System.String
IL_000b: call class [mscorlib]System.Type
[mscorlib]System.Type::GetTypeFromHandle(valuetype
[mscorlib]System.RuntimeTypeHandle)
IL_0010: call instance object
MapSolver.DynamicClasses.LayerFields::_getPropertyValue(string,

class [mscorlib]System.Type)
IL_0015: castclass [mscorlib]System.String
IL_001a: stloc.0
IL_001b: br.s IL_001d
IL_001d: ldloc.0
IL_001e: ret
} // end of method LayerFields::get_COLONIA



*************** Is this needed to code this and how ?

..method public hidebysig newslot specialname virtual final
instance void add_GetPropertyValue(class
[MyAssembly]MyAssembly.PropertyValueEventHandler 'value') cil managed
synchronized
{
// Code size 24 (0x18)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldarg.0
IL_0002: ldfld class
[MyAssembly]MyAssembly.PropertyValueEventHandler
MapSolver.DynamicClasses.LayerFields::GetPropertyValue
IL_0007: ldarg.1
IL_0008: call class [mscorlib]System.Delegate
[mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate,

class [mscorlib]System.Delegate)
IL_000d: castclass [MyAssembly]MyAssembly.PropertyValueEventHandler
IL_0012: stfld class
[MyAssembly]MyAssembly.PropertyValueEventHandler
MapSolver.DynamicClasses.LayerFields::GetPropertyValue
IL_0017: ret
} // end of method LayerFields::add_GetPropertyValue


**************** this is the counterpart to add_GetPropertyValue

..method public hidebysig newslot specialname virtual final
instance void remove_GetPropertyValue(class
[MyAssembly]MyAssembly.PropertyValueEventHandler 'value') cil managed
synchronized
{
// Code size 24 (0x18)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldarg.0
IL_0002: ldfld class
[MyAssembly]MyAssembly.PropertyValueEventHandler
MapSolver.DynamicClasses.LayerFields::GetPropertyValue
IL_0007: ldarg.1
IL_0008: call class [mscorlib]System.Delegate
[mscorlib]System.Delegate::Remove(class [mscorlib]System.Delegate,

class [mscorlib]System.Delegate)
IL_000d: castclass [MyAssembly]MyAssembly.PropertyValueEventHandler
IL_0012: stfld class
[MyAssembly]MyAssembly.PropertyValueEventHandler
MapSolver.DynamicClasses.LayerFields::GetPropertyValue
IL_0017: ret
} // end of method LayerFields::remove_GetPropertyValue



Up to now, I have created the following code, but I am stuck in the marked
code:


// create a dynamic assembly and module
AssemblyName an = new AssemblyName();
an.Name = "DynamicData";
AssemblyBuilder ab =
System.AppDomain.CurrentDomain.DefineDynamicAssembly(an,
AssemblyBuilderAccess.Run);
ModuleBuilder module = ab.DefineDynamicModule(ab.GetName().Name, false);

// create a new type to hold our Main method
TypeBuilder typeBuilder = module.DefineType("TableFields",
TypeAttributes.Public | TypeAttributes.Class |
TypeAttributes.AutoClass | TypeAttributes.AnsiClass |
TypeAttributes.BeforeFieldInit | TypeAttributes.AutoLayout,
typeof(object), new Type[]{typeof(MyAssembly.IDynamicDataFields)});

// Define three fields for this type

// field for the record number
FieldBuilder recordNo = typeBuilder.DefineField("_recordNo", typeof(int),
FieldAttributes.Private);

// field for GetPropertyValue
FieldBuilder getPropertyValue =
typeBuilder.DefineField("GetPropertyValue",
typeof(MyAssembly.PropertyValueEventHandler), FieldAttributes.Public);

// define the constructor

// el constructor que recibe el numero de registro
ConstructorBuilder cb =
typeBuilder.DefineConstructor(MethodAttributes.Public |
MethodAttributes.HideBySig |
MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
CallingConventions.Standard,
new Type[]{typeof(int)});

ILGenerator ilgen = cb.GetILGenerator();

// call the base constructor
// load "this"
ilgen.Emit(OpCodes.Ldarg_0);
// call base constructor
ilgen.Emit(OpCodes.Call, typeof(object).GetConstructor(new Type[0]));

// now save the record number in field "_recordNo"

// load "this"
ilgen.Emit(OpCodes.Ldarg_0);
// load first logical argument
ilgen.Emit(OpCodes.Ldarg_1);
// store in field _recordNo
ilgen.Emit(OpCodes.Stfld, recordNo);

// emit return code
ilgen.Emit(OpCodes.Ret);


// define the private method GetPropertyValue
MethodBuilder mb = typeBuilder.DefineMethod("_getPropertyValue",
MethodAttributes.Private | MethodAttributes.HideBySig,
CallingConventions.Standard,
typeof(object), new Type[]{typeof(string), typeof(System.Type)});

ilgen = mb.GetILGenerator();

// declare local variables
ilgen.DeclareLocal(typeof(MyAssembly.PropertyValueEventArgs));
ilgen.DeclareLocal(typeof(object));

// define "notnull" label
Label notnull_label = ilgen.DefineLabel();
Label exit_label = ilgen.DefineLabel();

// load "this"
ilgen.Emit(OpCodes.Ldarg_0);
// load "_getPropertyValue" field
ilgen.Emit(OpCodes.Ldfld, getPropertyValue);
// branch if != null
ilgen.Emit(OpCodes.Brtrue_S, notnull_label);
// load null
ilgen.Emit(OpCodes.Ldnull);
// set local variable 1 (result) to null
ilgen.Emit(OpCodes.Stloc_1);
// jump to exit_label
ilgen.Emit(OpCodes.Br_S, exit_label);

// mark "notnull_label
ilgen.MarkLabel(notnull_label);

// load "this"
ilgen.Emit(OpCodes.Ldarg_0);
// load _recordNo private type variable
ilgen.Emit(OpCodes.Ldfld, recordNo);
// load first argument - "propertyName"
ilgen.Emit(OpCodes.Ldarg_1);
// load second argument - "propertyType"
ilgen.Emit(OpCodes.Ldarg_2);
// load "null" as the default value of the property
ilgen.Emit(OpCodes.Ldnull);
// create the MyAssembly.PropertyValueEventArgs argument
ilgen.Emit(OpCodes.Newobj,
typeof(MyAssembly.PropertyValueEventArgs).GetConstructor(new
Type[]{typeof(int),
typeof(string), typeof(System.Type), typeof(object)}));
// store in local variable 0
ilgen.Emit(OpCodes.Stloc_0);

// load "this"
ilgen.Emit(OpCodes.Ldarg_0);
// load field getPropertyValue
ilgen.Emit(OpCodes.Ldfld, getPropertyValue);
// load "this"
ilgen.Emit(OpCodes.Ldarg_0);
// load local variable 0 - MyAssembly.PropertyValueEventArgs
ilgen.Emit(OpCodes.Ldloc_0);
// invoke the event handler in property
//***** I don't know how to code this line that corresponds to the below
line ==>> ilgen.Emit(OpCodes.Callvirt, how to ?
IL_0023: callvirt instance void
[MyAssembly]MyAssembly.PropertyValueEventHandler::Invoke(object, class
[MyAssembly]MyAssembly.PropertyValueEventArgs)

// load event args
ilgen.Emit(OpCodes.Ldloc_0);
ilgen.Emit(OpCodes.Callvirt,
typeof(MyAssembly.PropertyValueEventArgs).GetMethod("get_Value"));
ilgen.Emit(OpCodes.Stloc_1);
ilgen.Emit(OpCodes.Br_S, exit_label);
// mark label "exit"
ilgen.MarkLabel(exit_label);
ilgen.Emit(OpCodes.Ldloc_1);
// return
ilgen.Emit(OpCodes.Ret);




// now populate the list of properties from a database list of fields
for(int i = 0; i < DbTable.Fields.Count; i++)
{
Type t = null;
// map to our field types
switch(DbTable.Fields.FieldType)
{
case FieldType.String:
t = Type.GetType("System.String");
break;
case FieldType.Byte:
t=Type.GetType("System.Byte");
break;
case FieldType.Char:
t=Type.GetType("System.Char");
break;
case FieldType.Int16:
t=Type.GetType("System.Int16");
break;
case FieldType.Int32:
t=Type.GetType("System.Int32");
break;
case FieldType.Int64:
t=Type.GetType("System.Int64");
break;
case FieldType.Single:
t=Type.GetType("System.Single");
break;
case FieldType.Double:
t=Type.GetType("System.Double");
break;
case FieldType.Decimal:
t=Type.GetType("System.Decimal");
break;
case FieldType.Boolean:
t=Type.GetType("System.Boolean");
break;
case FieldType.DateTime:
t=Type.GetType("System.DateTime");
break;
case FieldType.Memo:
case FieldType.ByteArray:
t=Type.GetType("System.Byte[]");
break;
default:
t=Type.GetType("System.String");
break;
}
if(t == null)continue;

PropertyBuilder pb
=typeBuilder.DefineProperty(DbTable.Fields.FieldName,
System.Reflection.PropertyAttributes.None, t, new Type[]{});

MethodBuilder getMethod = typeBuilder.DefineMethod("get_" +
DbTable.Fields.FieldName,
MethodAttributes.Public, t, new Type[]{});

ilgen = getMethod.GetILGenerator();
ilgen.DeclareLocal(t);

// load "this"
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Ldloc_0);
// **** don't know how to code the following lines coming from
ildasm.exe
IL_000b: call class [mscorlib]System.Type
[mscorlib]System.Type::GetTypeFromHandle(valuetype
[mscorlib]System.RuntimeTypeHandle)
IL_0010: call instance object
MapSolver.DynamicClasses.LayerFields::_getPropertyValue(string,

class [mscorlib]System.Type)


}





There is also a SetPropertyValue method but with the GetProperty will be
enough help to me :)



Basically, I don't know how to code the following lines obtained from
ildasm.exe:



this used to invoke the event handler :

IL_0023: callvirt instance void
[MyAssembly]MyAssembly.PropertyValueEventHandler::Invoke(object, class
[MyAssembly]MyAssembly.PropertyValueEventArgs)



and these lines, used to obtain the "typeof(string)" or similar and the
other to call the _getPropertyValue method of this type.

IL_0006: ldtoken [mscorlib]System.String
IL_000b: call class [mscorlib]System.Type
[mscorlib]System.Type::GetTypeFromHandle(valuetype
[mscorlib]System.RuntimeTypeHandle)
IL_0010: call instance object
MapSolver.DynamicClasses.LayerFields::_getPropertyValue(string,

class [mscorlib]System.Type)



Someone could help or point me in the right direction ?

Thanks in Advance
Luis Arvayo

P.S. Sorry the long post :)
 
N

Nick Hounsome

Why use Reflection.Emit?
It is easy with System.CodeDom.

Luis Arvayo said:
Hi,

In c#, I need to dynamically create types at runtime that will consist of
the following:


- inherits from a given interface

- will have a constructor with an int argument

- Will have an event handler (derived from the interface)

- Will have a method that will return the value of the property or
properties based on firing the event handler and the hooked to the event
will return the value.


The prototype is something like the following:


// Event args for the event handler
public class PropertyValueEventArgs : EventArgs
{
int _recordNo;
public int RecordNo
{
get {return _recordNo;}
}

string _propertyName;
public string PropertyName
{
get {return _propertyName;}
}

Type _propertyType;
public Type PropertyType
{
get {return _propertyType;}
}

object _value;
public object Value
{
get {return _value;}
set {_value = value;}
}

public PropertyValueEventArgs(int recordNo, string propertyName, Type
propertyType, object value)
{
_recordNo = recordNo;
_propertyName = propertyName;
_propertyType = propertyType;
_value = value;
}
}

// delegate for the event handler

public delegate void PropertyValueEventHandler(object sender,
PropertyValueEventArgs e);

// The interfase that must be implemented by the dynamic type
public interface IDynamicDataFields
{
event PropertyValueEventHandler GetPropertyValue;
}

// note: the above two are already defined in other assembly


The prototye class that must be created at runtime :


public class LayerFields : IDynamicDataFields
{
// a private field
int _recordNo;

// the event
public event PropertyValueEventHandler GetPropertyValue;

// the constructor
public LayerFields(int recordNo)
{
_recordNo = recordNo;
}


// return value for the property based on the event
object _getPropertyValue(string propertyName, Type propertyType)
{
if(GetPropertyValue == null) return null;

PropertyValueEventArgs e = new PropertyValueEventArgs(_recordNo,
propertyName, propertyType, null);
GetPropertyValue(this, e);
return e.Value;
}


// here the list of variable number of properties of type string, int,
bool, and so on.

public string StringProp
{
get {return (string)_getPropertyValue("StringProp", typeof(string));}
}

public int IntProp
{
get (return (int)_getPropertyValue("IntProp", typeof(int));}
}

... and so on
}


This is the opcode generated by ildasm.exe

********* constructor

.method public hidebysig specialname rtspecialname
instance void .ctor(int32 recordNo) cil managed
{
// Code size 14 (0xe)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ldarg.0
IL_0007: ldarg.1
IL_0008: stfld int32
MapSolver.DynamicClasses.LayerFields::_recordNo
IL_000d: ret
} // end of method LayerFields::.ctor


********** _getPropertyValue method


.method private hidebysig instance object
_getPropertyValue(string propertyName,
class [mscorlib]System.Type propertyType) cil
managed
{
// Code size 51 (0x33)
.maxstack 5
.locals ([0] class [MyAssembly]MyAssembly.PropertyValueEventArgs e,
[1] object CS$00000003$00000000)
IL_0000: ldarg.0
IL_0001: ldfld class
[MyAssembly]MyAssembly.PropertyValueEventHandler
MapSolver.DynamicClasses.LayerFields::GetPropertyValue
IL_0006: brtrue.s IL_000c
IL_0008: ldnull
IL_0009: stloc.1
IL_000a: br.s IL_0031
IL_000c: ldarg.0
IL_000d: ldfld int32
MapSolver.DynamicClasses.LayerFields::_recordNo
IL_0012: ldarg.1
IL_0013: ldarg.2
IL_0014: ldnull
IL_0015: newobj instance void
[MyAssembly]MyAssembly.PropertyValueEventArgs::.ctor(int32,

string,

class [mscorlib]System.Type,

object)
IL_001a: stloc.0
IL_001b: ldarg.0
IL_001c: ldfld class
[MyAssembly]MyAssembly.PropertyValueEventHandler
MapSolver.DynamicClasses.LayerFields::GetPropertyValue
IL_0021: ldarg.0
IL_0022: ldloc.0
IL_0023: callvirt instance void
[MyAssembly]MyAssembly.PropertyValueEventHandler::Invoke(object,

class [MyAssembly]MyAssembly.PropertyValueEventArgs)
IL_0028: ldloc.0
IL_0029: callvirt instance object
[MyAssembly]MyAssembly.PropertyValueEventArgs::get_Value()
IL_002e: stloc.1
IL_002f: br.s IL_0031
IL_0031: ldloc.1
IL_0032: ret
} // end of method LayerFields::_getPropertyValue


**************** return the property value


.method public hidebysig specialname instance string
get_StringProp() cil managed
{
// Code size 31 (0x1f)
.maxstack 3
.locals ([0] string CS$00000003$00000000)
IL_0000: ldarg.0
IL_0001: ldstr "StringProp"
IL_0006: ldtoken [mscorlib]System.String
IL_000b: call class [mscorlib]System.Type
[mscorlib]System.Type::GetTypeFromHandle(valuetype
[mscorlib]System.RuntimeTypeHandle)
IL_0010: call instance object
MapSolver.DynamicClasses.LayerFields::_getPropertyValue(string,

class [mscorlib]System.Type)
IL_0015: castclass [mscorlib]System.String
IL_001a: stloc.0
IL_001b: br.s IL_001d
IL_001d: ldloc.0
IL_001e: ret
} // end of method LayerFields::get_COLONIA



*************** Is this needed to code this and how ?

.method public hidebysig newslot specialname virtual final
instance void add_GetPropertyValue(class
[MyAssembly]MyAssembly.PropertyValueEventHandler 'value') cil managed
synchronized
{
// Code size 24 (0x18)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldarg.0
IL_0002: ldfld class
[MyAssembly]MyAssembly.PropertyValueEventHandler
MapSolver.DynamicClasses.LayerFields::GetPropertyValue
IL_0007: ldarg.1
IL_0008: call class [mscorlib]System.Delegate
[mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate,

class [mscorlib]System.Delegate)
IL_000d: castclass [MyAssembly]MyAssembly.PropertyValueEventHandler
IL_0012: stfld class
[MyAssembly]MyAssembly.PropertyValueEventHandler
MapSolver.DynamicClasses.LayerFields::GetPropertyValue
IL_0017: ret
} // end of method LayerFields::add_GetPropertyValue


**************** this is the counterpart to add_GetPropertyValue

.method public hidebysig newslot specialname virtual final
instance void remove_GetPropertyValue(class
[MyAssembly]MyAssembly.PropertyValueEventHandler 'value') cil managed
synchronized
{
// Code size 24 (0x18)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldarg.0
IL_0002: ldfld class
[MyAssembly]MyAssembly.PropertyValueEventHandler
MapSolver.DynamicClasses.LayerFields::GetPropertyValue
IL_0007: ldarg.1
IL_0008: call class [mscorlib]System.Delegate
[mscorlib]System.Delegate::Remove(class [mscorlib]System.Delegate,

class [mscorlib]System.Delegate)
IL_000d: castclass [MyAssembly]MyAssembly.PropertyValueEventHandler
IL_0012: stfld class
[MyAssembly]MyAssembly.PropertyValueEventHandler
MapSolver.DynamicClasses.LayerFields::GetPropertyValue
IL_0017: ret
} // end of method LayerFields::remove_GetPropertyValue



Up to now, I have created the following code, but I am stuck in the marked
code:


// create a dynamic assembly and module
AssemblyName an = new AssemblyName();
an.Name = "DynamicData";
AssemblyBuilder ab =
System.AppDomain.CurrentDomain.DefineDynamicAssembly(an,
AssemblyBuilderAccess.Run);
ModuleBuilder module = ab.DefineDynamicModule(ab.GetName().Name, false);

// create a new type to hold our Main method
TypeBuilder typeBuilder = module.DefineType("TableFields",
TypeAttributes.Public | TypeAttributes.Class |
TypeAttributes.AutoClass | TypeAttributes.AnsiClass |
TypeAttributes.BeforeFieldInit | TypeAttributes.AutoLayout,
typeof(object), new Type[]{typeof(MyAssembly.IDynamicDataFields)});

// Define three fields for this type

// field for the record number
FieldBuilder recordNo = typeBuilder.DefineField("_recordNo",
typeof(int), FieldAttributes.Private);

// field for GetPropertyValue
FieldBuilder getPropertyValue =
typeBuilder.DefineField("GetPropertyValue",
typeof(MyAssembly.PropertyValueEventHandler), FieldAttributes.Public);

// define the constructor

// el constructor que recibe el numero de registro
ConstructorBuilder cb =
typeBuilder.DefineConstructor(MethodAttributes.Public |
MethodAttributes.HideBySig |
MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
CallingConventions.Standard,
new Type[]{typeof(int)});

ILGenerator ilgen = cb.GetILGenerator();

// call the base constructor
// load "this"
ilgen.Emit(OpCodes.Ldarg_0);
// call base constructor
ilgen.Emit(OpCodes.Call, typeof(object).GetConstructor(new Type[0]));

// now save the record number in field "_recordNo"

// load "this"
ilgen.Emit(OpCodes.Ldarg_0);
// load first logical argument
ilgen.Emit(OpCodes.Ldarg_1);
// store in field _recordNo
ilgen.Emit(OpCodes.Stfld, recordNo);

// emit return code
ilgen.Emit(OpCodes.Ret);


// define the private method GetPropertyValue
MethodBuilder mb = typeBuilder.DefineMethod("_getPropertyValue",
MethodAttributes.Private | MethodAttributes.HideBySig,
CallingConventions.Standard,
typeof(object), new Type[]{typeof(string), typeof(System.Type)});

ilgen = mb.GetILGenerator();

// declare local variables
ilgen.DeclareLocal(typeof(MyAssembly.PropertyValueEventArgs));
ilgen.DeclareLocal(typeof(object));

// define "notnull" label
Label notnull_label = ilgen.DefineLabel();
Label exit_label = ilgen.DefineLabel();

// load "this"
ilgen.Emit(OpCodes.Ldarg_0);
// load "_getPropertyValue" field
ilgen.Emit(OpCodes.Ldfld, getPropertyValue);
// branch if != null
ilgen.Emit(OpCodes.Brtrue_S, notnull_label);
// load null
ilgen.Emit(OpCodes.Ldnull);
// set local variable 1 (result) to null
ilgen.Emit(OpCodes.Stloc_1);
// jump to exit_label
ilgen.Emit(OpCodes.Br_S, exit_label);

// mark "notnull_label
ilgen.MarkLabel(notnull_label);

// load "this"
ilgen.Emit(OpCodes.Ldarg_0);
// load _recordNo private type variable
ilgen.Emit(OpCodes.Ldfld, recordNo);
// load first argument - "propertyName"
ilgen.Emit(OpCodes.Ldarg_1);
// load second argument - "propertyType"
ilgen.Emit(OpCodes.Ldarg_2);
// load "null" as the default value of the property
ilgen.Emit(OpCodes.Ldnull);
// create the MyAssembly.PropertyValueEventArgs argument
ilgen.Emit(OpCodes.Newobj,
typeof(MyAssembly.PropertyValueEventArgs).GetConstructor(new
Type[]{typeof(int),
typeof(string), typeof(System.Type), typeof(object)}));
// store in local variable 0
ilgen.Emit(OpCodes.Stloc_0);

// load "this"
ilgen.Emit(OpCodes.Ldarg_0);
// load field getPropertyValue
ilgen.Emit(OpCodes.Ldfld, getPropertyValue);
// load "this"
ilgen.Emit(OpCodes.Ldarg_0);
// load local variable 0 - MyAssembly.PropertyValueEventArgs
ilgen.Emit(OpCodes.Ldloc_0);
// invoke the event handler in property
//***** I don't know how to code this line that corresponds to the below
line ==>> ilgen.Emit(OpCodes.Callvirt, how to ?
IL_0023: callvirt instance void
[MyAssembly]MyAssembly.PropertyValueEventHandler::Invoke(object, class
[MyAssembly]MyAssembly.PropertyValueEventArgs)

// load event args
ilgen.Emit(OpCodes.Ldloc_0);
ilgen.Emit(OpCodes.Callvirt,
typeof(MyAssembly.PropertyValueEventArgs).GetMethod("get_Value"));
ilgen.Emit(OpCodes.Stloc_1);
ilgen.Emit(OpCodes.Br_S, exit_label);
// mark label "exit"
ilgen.MarkLabel(exit_label);
ilgen.Emit(OpCodes.Ldloc_1);
// return
ilgen.Emit(OpCodes.Ret);




// now populate the list of properties from a database list of fields
for(int i = 0; i < DbTable.Fields.Count; i++)
{
Type t = null;
// map to our field types
switch(DbTable.Fields.FieldType)
{
case FieldType.String:
t = Type.GetType("System.String");
break;
case FieldType.Byte:
t=Type.GetType("System.Byte");
break;
case FieldType.Char:
t=Type.GetType("System.Char");
break;
case FieldType.Int16:
t=Type.GetType("System.Int16");
break;
case FieldType.Int32:
t=Type.GetType("System.Int32");
break;
case FieldType.Int64:
t=Type.GetType("System.Int64");
break;
case FieldType.Single:
t=Type.GetType("System.Single");
break;
case FieldType.Double:
t=Type.GetType("System.Double");
break;
case FieldType.Decimal:
t=Type.GetType("System.Decimal");
break;
case FieldType.Boolean:
t=Type.GetType("System.Boolean");
break;
case FieldType.DateTime:
t=Type.GetType("System.DateTime");
break;
case FieldType.Memo:
case FieldType.ByteArray:
t=Type.GetType("System.Byte[]");
break;
default:
t=Type.GetType("System.String");
break;
}
if(t == null)continue;

PropertyBuilder pb
=typeBuilder.DefineProperty(DbTable.Fields.FieldName,
System.Reflection.PropertyAttributes.None, t, new Type[]{});

MethodBuilder getMethod = typeBuilder.DefineMethod("get_" +
DbTable.Fields.FieldName,
MethodAttributes.Public, t, new Type[]{});

ilgen = getMethod.GetILGenerator();
ilgen.DeclareLocal(t);

// load "this"
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Ldloc_0);
// **** don't know how to code the following lines coming from
ildasm.exe
IL_000b: call class [mscorlib]System.Type
[mscorlib]System.Type::GetTypeFromHandle(valuetype
[mscorlib]System.RuntimeTypeHandle)
IL_0010: call instance object
MapSolver.DynamicClasses.LayerFields::_getPropertyValue(string,

class [mscorlib]System.Type)


}





There is also a SetPropertyValue method but with the GetProperty will be
enough help to me :)



Basically, I don't know how to code the following lines obtained from
ildasm.exe:



this used to invoke the event handler :

IL_0023: callvirt instance void
[MyAssembly]MyAssembly.PropertyValueEventHandler::Invoke(object, class
[MyAssembly]MyAssembly.PropertyValueEventArgs)



and these lines, used to obtain the "typeof(string)" or similar and the
other to call the _getPropertyValue method of this type.

IL_0006: ldtoken [mscorlib]System.String
IL_000b: call class [mscorlib]System.Type
[mscorlib]System.Type::GetTypeFromHandle(valuetype
[mscorlib]System.RuntimeTypeHandle)
IL_0010: call instance object
MapSolver.DynamicClasses.LayerFields::_getPropertyValue(string,

class [mscorlib]System.Type)



Someone could help or point me in the right direction ?

Thanks in Advance
Luis Arvayo

P.S. Sorry the long post :)
 
L

Luis Arvayo

Why use Reflection.Emit?
It is easy with System.CodeDom.


Why ? Because it is said that the result is more optimized than using
CodeDom. Is not that true ?

Also, it is said that by using CodeDom a new assembly must be created the
next time I need another dynamic type which is not true for Reflection.Emit
where the assembly could be reused. In this app, I need to create a lot of
dynamic types and at different moments in lifetime of app.


Nick Hounsome said:
Why use Reflection.Emit?
It is easy with System.CodeDom.

Luis Arvayo said:
Hi,

In c#, I need to dynamically create types at runtime that will consist of
the following:


- inherits from a given interface

- will have a constructor with an int argument

- Will have an event handler (derived from the interface)

- Will have a method that will return the value of the property or
properties based on firing the event handler and the hooked to the event
will return the value.


The prototype is something like the following:


// Event args for the event handler
public class PropertyValueEventArgs : EventArgs
{
int _recordNo;
public int RecordNo
{
get {return _recordNo;}
}

string _propertyName;
public string PropertyName
{
get {return _propertyName;}
}

Type _propertyType;
public Type PropertyType
{
get {return _propertyType;}
}

object _value;
public object Value
{
get {return _value;}
set {_value = value;}
}

public PropertyValueEventArgs(int recordNo, string propertyName, Type
propertyType, object value)
{
_recordNo = recordNo;
_propertyName = propertyName;
_propertyType = propertyType;
_value = value;
}
}

// delegate for the event handler

public delegate void PropertyValueEventHandler(object sender,
PropertyValueEventArgs e);

// The interfase that must be implemented by the dynamic type
public interface IDynamicDataFields
{
event PropertyValueEventHandler GetPropertyValue;
}

// note: the above two are already defined in other assembly


The prototye class that must be created at runtime :


public class LayerFields : IDynamicDataFields
{
// a private field
int _recordNo;

// the event
public event PropertyValueEventHandler GetPropertyValue;

// the constructor
public LayerFields(int recordNo)
{
_recordNo = recordNo;
}


// return value for the property based on the event
object _getPropertyValue(string propertyName, Type propertyType)
{
if(GetPropertyValue == null) return null;

PropertyValueEventArgs e = new PropertyValueEventArgs(_recordNo,
propertyName, propertyType, null);
GetPropertyValue(this, e);
return e.Value;
}


// here the list of variable number of properties of type string, int,
bool, and so on.

public string StringProp
{
get {return (string)_getPropertyValue("StringProp", typeof(string));}
}

public int IntProp
{
get (return (int)_getPropertyValue("IntProp", typeof(int));}
}

... and so on
}


This is the opcode generated by ildasm.exe

********* constructor

.method public hidebysig specialname rtspecialname
instance void .ctor(int32 recordNo) cil managed
{
// Code size 14 (0xe)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ldarg.0
IL_0007: ldarg.1
IL_0008: stfld int32
MapSolver.DynamicClasses.LayerFields::_recordNo
IL_000d: ret
} // end of method LayerFields::.ctor


********** _getPropertyValue method


.method private hidebysig instance object
_getPropertyValue(string propertyName,
class [mscorlib]System.Type propertyType) cil
managed
{
// Code size 51 (0x33)
.maxstack 5
.locals ([0] class [MyAssembly]MyAssembly.PropertyValueEventArgs e,
[1] object CS$00000003$00000000)
IL_0000: ldarg.0
IL_0001: ldfld class
[MyAssembly]MyAssembly.PropertyValueEventHandler
MapSolver.DynamicClasses.LayerFields::GetPropertyValue
IL_0006: brtrue.s IL_000c
IL_0008: ldnull
IL_0009: stloc.1
IL_000a: br.s IL_0031
IL_000c: ldarg.0
IL_000d: ldfld int32
MapSolver.DynamicClasses.LayerFields::_recordNo
IL_0012: ldarg.1
IL_0013: ldarg.2
IL_0014: ldnull
IL_0015: newobj instance void
[MyAssembly]MyAssembly.PropertyValueEventArgs::.ctor(int32,

string,

class [mscorlib]System.Type,

object)
IL_001a: stloc.0
IL_001b: ldarg.0
IL_001c: ldfld class
[MyAssembly]MyAssembly.PropertyValueEventHandler
MapSolver.DynamicClasses.LayerFields::GetPropertyValue
IL_0021: ldarg.0
IL_0022: ldloc.0
IL_0023: callvirt instance void
[MyAssembly]MyAssembly.PropertyValueEventHandler::Invoke(object,

class [MyAssembly]MyAssembly.PropertyValueEventArgs)
IL_0028: ldloc.0
IL_0029: callvirt instance object
[MyAssembly]MyAssembly.PropertyValueEventArgs::get_Value()
IL_002e: stloc.1
IL_002f: br.s IL_0031
IL_0031: ldloc.1
IL_0032: ret
} // end of method LayerFields::_getPropertyValue


**************** return the property value


.method public hidebysig specialname instance string
get_StringProp() cil managed
{
// Code size 31 (0x1f)
.maxstack 3
.locals ([0] string CS$00000003$00000000)
IL_0000: ldarg.0
IL_0001: ldstr "StringProp"
IL_0006: ldtoken [mscorlib]System.String
IL_000b: call class [mscorlib]System.Type
[mscorlib]System.Type::GetTypeFromHandle(valuetype
[mscorlib]System.RuntimeTypeHandle)
IL_0010: call instance object
MapSolver.DynamicClasses.LayerFields::_getPropertyValue(string,

class [mscorlib]System.Type)
IL_0015: castclass [mscorlib]System.String
IL_001a: stloc.0
IL_001b: br.s IL_001d
IL_001d: ldloc.0
IL_001e: ret
} // end of method LayerFields::get_COLONIA



*************** Is this needed to code this and how ?

.method public hidebysig newslot specialname virtual final
instance void add_GetPropertyValue(class
[MyAssembly]MyAssembly.PropertyValueEventHandler 'value') cil managed
synchronized
{
// Code size 24 (0x18)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldarg.0
IL_0002: ldfld class
[MyAssembly]MyAssembly.PropertyValueEventHandler
MapSolver.DynamicClasses.LayerFields::GetPropertyValue
IL_0007: ldarg.1
IL_0008: call class [mscorlib]System.Delegate
[mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate,

class [mscorlib]System.Delegate)
IL_000d: castclass [MyAssembly]MyAssembly.PropertyValueEventHandler
IL_0012: stfld class
[MyAssembly]MyAssembly.PropertyValueEventHandler
MapSolver.DynamicClasses.LayerFields::GetPropertyValue
IL_0017: ret
} // end of method LayerFields::add_GetPropertyValue


**************** this is the counterpart to add_GetPropertyValue

.method public hidebysig newslot specialname virtual final
instance void remove_GetPropertyValue(class
[MyAssembly]MyAssembly.PropertyValueEventHandler 'value') cil managed
synchronized
{
// Code size 24 (0x18)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldarg.0
IL_0002: ldfld class
[MyAssembly]MyAssembly.PropertyValueEventHandler
MapSolver.DynamicClasses.LayerFields::GetPropertyValue
IL_0007: ldarg.1
IL_0008: call class [mscorlib]System.Delegate
[mscorlib]System.Delegate::Remove(class [mscorlib]System.Delegate,

class [mscorlib]System.Delegate)
IL_000d: castclass [MyAssembly]MyAssembly.PropertyValueEventHandler
IL_0012: stfld class
[MyAssembly]MyAssembly.PropertyValueEventHandler
MapSolver.DynamicClasses.LayerFields::GetPropertyValue
IL_0017: ret
} // end of method LayerFields::remove_GetPropertyValue



Up to now, I have created the following code, but I am stuck in the
marked code:


// create a dynamic assembly and module
AssemblyName an = new AssemblyName();
an.Name = "DynamicData";
AssemblyBuilder ab =
System.AppDomain.CurrentDomain.DefineDynamicAssembly(an,
AssemblyBuilderAccess.Run);
ModuleBuilder module = ab.DefineDynamicModule(ab.GetName().Name,
false);

// create a new type to hold our Main method
TypeBuilder typeBuilder = module.DefineType("TableFields",
TypeAttributes.Public | TypeAttributes.Class |
TypeAttributes.AutoClass | TypeAttributes.AnsiClass |
TypeAttributes.BeforeFieldInit | TypeAttributes.AutoLayout,
typeof(object), new Type[]{typeof(MyAssembly.IDynamicDataFields)});

// Define three fields for this type

// field for the record number
FieldBuilder recordNo = typeBuilder.DefineField("_recordNo",
typeof(int), FieldAttributes.Private);

// field for GetPropertyValue
FieldBuilder getPropertyValue =
typeBuilder.DefineField("GetPropertyValue",
typeof(MyAssembly.PropertyValueEventHandler), FieldAttributes.Public);

// define the constructor

// el constructor que recibe el numero de registro
ConstructorBuilder cb =
typeBuilder.DefineConstructor(MethodAttributes.Public |
MethodAttributes.HideBySig |
MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
CallingConventions.Standard,
new Type[]{typeof(int)});

ILGenerator ilgen = cb.GetILGenerator();

// call the base constructor
// load "this"
ilgen.Emit(OpCodes.Ldarg_0);
// call base constructor
ilgen.Emit(OpCodes.Call, typeof(object).GetConstructor(new Type[0]));

// now save the record number in field "_recordNo"

// load "this"
ilgen.Emit(OpCodes.Ldarg_0);
// load first logical argument
ilgen.Emit(OpCodes.Ldarg_1);
// store in field _recordNo
ilgen.Emit(OpCodes.Stfld, recordNo);

// emit return code
ilgen.Emit(OpCodes.Ret);


// define the private method GetPropertyValue
MethodBuilder mb = typeBuilder.DefineMethod("_getPropertyValue",
MethodAttributes.Private | MethodAttributes.HideBySig,
CallingConventions.Standard,
typeof(object), new Type[]{typeof(string), typeof(System.Type)});

ilgen = mb.GetILGenerator();

// declare local variables
ilgen.DeclareLocal(typeof(MyAssembly.PropertyValueEventArgs));
ilgen.DeclareLocal(typeof(object));

// define "notnull" label
Label notnull_label = ilgen.DefineLabel();
Label exit_label = ilgen.DefineLabel();

// load "this"
ilgen.Emit(OpCodes.Ldarg_0);
// load "_getPropertyValue" field
ilgen.Emit(OpCodes.Ldfld, getPropertyValue);
// branch if != null
ilgen.Emit(OpCodes.Brtrue_S, notnull_label);
// load null
ilgen.Emit(OpCodes.Ldnull);
// set local variable 1 (result) to null
ilgen.Emit(OpCodes.Stloc_1);
// jump to exit_label
ilgen.Emit(OpCodes.Br_S, exit_label);

// mark "notnull_label
ilgen.MarkLabel(notnull_label);

// load "this"
ilgen.Emit(OpCodes.Ldarg_0);
// load _recordNo private type variable
ilgen.Emit(OpCodes.Ldfld, recordNo);
// load first argument - "propertyName"
ilgen.Emit(OpCodes.Ldarg_1);
// load second argument - "propertyType"
ilgen.Emit(OpCodes.Ldarg_2);
// load "null" as the default value of the property
ilgen.Emit(OpCodes.Ldnull);
// create the MyAssembly.PropertyValueEventArgs argument
ilgen.Emit(OpCodes.Newobj,
typeof(MyAssembly.PropertyValueEventArgs).GetConstructor(new
Type[]{typeof(int),
typeof(string), typeof(System.Type), typeof(object)}));
// store in local variable 0
ilgen.Emit(OpCodes.Stloc_0);

// load "this"
ilgen.Emit(OpCodes.Ldarg_0);
// load field getPropertyValue
ilgen.Emit(OpCodes.Ldfld, getPropertyValue);
// load "this"
ilgen.Emit(OpCodes.Ldarg_0);
// load local variable 0 - MyAssembly.PropertyValueEventArgs
ilgen.Emit(OpCodes.Ldloc_0);
// invoke the event handler in property
//***** I don't know how to code this line that corresponds to the
below line ==>> ilgen.Emit(OpCodes.Callvirt, how to ?
IL_0023: callvirt instance void
[MyAssembly]MyAssembly.PropertyValueEventHandler::Invoke(object, class
[MyAssembly]MyAssembly.PropertyValueEventArgs)

// load event args
ilgen.Emit(OpCodes.Ldloc_0);
ilgen.Emit(OpCodes.Callvirt,
typeof(MyAssembly.PropertyValueEventArgs).GetMethod("get_Value"));
ilgen.Emit(OpCodes.Stloc_1);
ilgen.Emit(OpCodes.Br_S, exit_label);
// mark label "exit"
ilgen.MarkLabel(exit_label);
ilgen.Emit(OpCodes.Ldloc_1);
// return
ilgen.Emit(OpCodes.Ret);




// now populate the list of properties from a database list of fields
for(int i = 0; i < DbTable.Fields.Count; i++)
{
Type t = null;
// map to our field types
switch(DbTable.Fields.FieldType)
{
case FieldType.String:
t = Type.GetType("System.String");
break;
case FieldType.Byte:
t=Type.GetType("System.Byte");
break;
case FieldType.Char:
t=Type.GetType("System.Char");
break;
case FieldType.Int16:
t=Type.GetType("System.Int16");
break;
case FieldType.Int32:
t=Type.GetType("System.Int32");
break;
case FieldType.Int64:
t=Type.GetType("System.Int64");
break;
case FieldType.Single:
t=Type.GetType("System.Single");
break;
case FieldType.Double:
t=Type.GetType("System.Double");
break;
case FieldType.Decimal:
t=Type.GetType("System.Decimal");
break;
case FieldType.Boolean:
t=Type.GetType("System.Boolean");
break;
case FieldType.DateTime:
t=Type.GetType("System.DateTime");
break;
case FieldType.Memo:
case FieldType.ByteArray:
t=Type.GetType("System.Byte[]");
break;
default:
t=Type.GetType("System.String");
break;
}
if(t == null)continue;

PropertyBuilder pb
=typeBuilder.DefineProperty(DbTable.Fields.FieldName,
System.Reflection.PropertyAttributes.None, t, new Type[]{});

MethodBuilder getMethod = typeBuilder.DefineMethod("get_" +
DbTable.Fields.FieldName,
MethodAttributes.Public, t, new Type[]{});

ilgen = getMethod.GetILGenerator();
ilgen.DeclareLocal(t);

// load "this"
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Ldloc_0);
// **** don't know how to code the following lines coming from
ildasm.exe
IL_000b: call class [mscorlib]System.Type
[mscorlib]System.Type::GetTypeFromHandle(valuetype
[mscorlib]System.RuntimeTypeHandle)
IL_0010: call instance object
MapSolver.DynamicClasses.LayerFields::_getPropertyValue(string,

class [mscorlib]System.Type)


}





There is also a SetPropertyValue method but with the GetProperty will be
enough help to me :)



Basically, I don't know how to code the following lines obtained from
ildasm.exe:



this used to invoke the event handler :

IL_0023: callvirt instance void
[MyAssembly]MyAssembly.PropertyValueEventHandler::Invoke(object, class
[MyAssembly]MyAssembly.PropertyValueEventArgs)



and these lines, used to obtain the "typeof(string)" or similar and the
other to call the _getPropertyValue method of this type.

IL_0006: ldtoken [mscorlib]System.String
IL_000b: call class [mscorlib]System.Type
[mscorlib]System.Type::GetTypeFromHandle(valuetype
[mscorlib]System.RuntimeTypeHandle)
IL_0010: call instance object
MapSolver.DynamicClasses.LayerFields::_getPropertyValue(string,

class [mscorlib]System.Type)



Someone could help or point me in the right direction ?

Thanks in Advance
Luis Arvayo

P.S. Sorry the long post :)

 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top