Problem is that you are trying to use local variable that not declared:
getIl.Emit(OpCodes.Ldloca_S, 0); // load address of local variable with index 0 on stack
getIl.Emit(OpCodes.Initobj, typeof(int?)); // initialize local variable
getIl.Emit(OpCodes.Ldloc_0); // load value of local variable with index 0 on stack
You can define required local variable like this:
var local = getIl.DeclareLocal(typeof(int?));
And your code will be valid, but to improve readability I would advise you to use variable local
instead index. It could be done like this:
getIl.Emit(OpCodes.Ldloca_S, local); // load address of local variable on stack
getIl.Emit(OpCodes.Initobj, typeof(int?)); // initialize local variable
getIl.Emit(OpCodes.Ldloc, local); // load value of local variable on stack
P.S. I'll put here code that I've used for generation of classes Hist
and ProcessData
, may be it could be useful for you in some way, in case if my explanation was not sufficient.
Main logic in this helper for property creation:
public static class TypeBuilderExtensions
{
public static PropertyBuilder CreateProperty<T>(this TypeBuilder builder, string name) => CreateProperty(builder, typeof(T), name);
public static PropertyBuilder CreateProperty(this TypeBuilder builder, Type propertyType, string name)
{
var field = builder.DefineField($"_{name}", propertyType, FieldAttributes.Private);
var getMethodBuilder = builder.DefineMethod($"get_{name}", MethodAttributes.Public, propertyType, Type.EmptyTypes);
var getGenerator = getMethodBuilder.GetILGenerator();
getGenerator.Emit(OpCodes.Ldarg_0);
getGenerator.Emit(OpCodes.Ldfld, field);
getGenerator.Emit(OpCodes.Ret);
var setMethodBuilder = builder.DefineMethod($"set_{name}", MethodAttributes.Public, typeof(void), new[] { propertyType });
var setGenerator = setMethodBuilder.GetILGenerator();
setGenerator.Emit(OpCodes.Ldarg_0);
setGenerator.Emit(OpCodes.Ldarg_1);
setGenerator.Emit(OpCodes.Stfld, field);
setGenerator.Emit(OpCodes.Ret);
var propertyBuilder = builder.DefineProperty(name, PropertyAttributes.None, propertyType, Type.EmptyTypes);
propertyBuilder.SetGetMethod(getMethodBuilder);
propertyBuilder.SetSetMethod(setMethodBuilder);
return propertyBuilder;
}
public static PropertyBuilder CreateCalculatedProperty<T>(this TypeBuilder builder, string name, MethodInfo getObject, MethodInfo getObjectProperty) => CreateCalculatedProperty(builder, typeof(T), name, getObject, getObjectProperty);
public static PropertyBuilder CreateCalculatedProperty(this TypeBuilder builder, Type propertyType, string name, MethodInfo getObject, MethodInfo getObjectProperty)
{
var getMethodBuilder = builder.DefineMethod($"get_{name}", MethodAttributes.Public, propertyType, Type.EmptyTypes);
var getGenerator = getMethodBuilder.GetILGenerator();
var label = getGenerator.DefineLabel();
var local = getGenerator.DeclareLocal(propertyType);
getGenerator.Emit(OpCodes.Ldarg_0);
getGenerator.Emit(OpCodes.Callvirt, getObject);
getGenerator.Emit(OpCodes.Brtrue, label);
getGenerator.Emit(OpCodes.Ldloca_S, local);
getGenerator.Emit(OpCodes.Initobj, propertyType);
getGenerator.Emit(OpCodes.Ldloc, local);
getGenerator.Emit(OpCodes.Ret);
getGenerator.MarkLabel(label);
getGenerator.Emit(OpCodes.Ldarg_0);
getGenerator.Emit(OpCodes.Callvirt, getObject);
getGenerator.Emit(OpCodes.Callvirt, getObjectProperty);
getGenerator.Emit(OpCodes.Ret);
var propertyBuilder = builder.DefineProperty(name, PropertyAttributes.None, propertyType, Type.EmptyTypes);
propertyBuilder.SetGetMethod(getMethodBuilder);
return propertyBuilder;
}
}
Here is class creation itself:
var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("TestAssembly"), AssemblyBuilderAccess.Run);
var moduleBuilder = assemblyBuilder.DefineDynamicModule("TestModule");
var histBuilder = moduleBuilder.DefineType("Hist");
var minProperty = histBuilder.CreateProperty<int?>("min");
var maxProperty = histBuilder.CreateProperty<int?>("max");
var processDataBuilder = moduleBuilder.DefineType("ProcessData");
var histProperty = processDataBuilder.CreateProperty(histBuilder, "hist");
processDataBuilder.CreateProperty<string>("id");
processDataBuilder.CreateProperty<string>("source");
processDataBuilder.CreateProperty<int?>("currentValue");
processDataBuilder.CreateCalculatedProperty<int?>("min", histProperty.GetMethod, minProperty.GetMethod);
processDataBuilder.CreateCalculatedProperty<int?>("max", histProperty.GetMethod, maxProperty.GetMethod);
And finally primitive validation of created classes:
void ValidateProperty(object instance, string name, object value, bool setValue = true)
{
var type = instance.GetType();
var property = type.GetProperty(name);
if (setValue) property.SetValue(instance, value);
var result = property.GetValue(instance);
var equals = property.PropertyType.IsValueType && value != null ? value.Equals(result) : value == result;
if (!equals)
throw new InvalidDataException("Property not valid");
}
var histType = histBuilder.CreateType();
var histInstance = Activator.CreateInstance(histType);
ValidateProperty(histInstance, "min", 12);
ValidateProperty(histInstance, "max", 21);
var processDataType = processDataBuilder.CreateType();
var processDataInstance = Activator.CreateInstance(processDataType);
ValidateProperty(processDataInstance, "hist", histInstance);
ValidateProperty(processDataInstance, "id", "Test!");
ValidateProperty(processDataInstance, "source", "Source#");
ValidateProperty(processDataInstance, "currentValue", 126);
ValidateProperty(processDataInstance, "min", 12, false);
ValidateProperty(processDataInstance, "max", 21, false);
ValidateProperty(processDataInstance, "hist", null);
ValidateProperty(processDataInstance, "min", null, false);
ValidateProperty(processDataInstance, "max", null, false);