цель: динамическое создание объекта из интерфейса и установка значений из IDataReader
сейчас я могу динамически создавать тип / экземпляр объекта из интерфейса, но когда я устанавливаю значение из IDataReader, возникает ошибка:
«указанное приведение недействительно»
Я гуглил это, но не так сильно помог. я знаю, что ошибка связана с кодом emit, но я не знаю, как его изменить.
код динамического создания типа из интерфейса
private PropertyBuilder BuildProperty(TypeBuilder tb, PropertyInfo property)
{
var fieldName = $"_{property.Name}";
var propertyBuilder = tb.DefineProperty(property.Name, PropertyAttributes.None, property.PropertyType, Type.EmptyTypes);
var fb = tb.DefineField(fieldName, property.PropertyType, FieldAttributes.Private);
var getSetAttributes = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig | MethodAttributes.Virtual;
var getterBuilder = BuildGetter(tb, property, fb, getSetAttributes);
var setterBuilder = BuildSetter(tb, property, fb, getSetAttributes);
propertyBuilder.SetGetMethod(getterBuilder);
propertyBuilder.SetSetMethod(setterBuilder);
return propertyBuilder;
}
private MethodBuilder BuildGetter(TypeBuilder tb, PropertyInfo property, FieldBuilder fb, MethodAttributes attributes)
{
var getterBuilder = tb.DefineMethod($"get_{property.Name}", attributes, property.PropertyType, Type.EmptyTypes);
var ilGenerator = getterBuilder.GetILGenerator();
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Ldfld, fb);
ilGenerator.Emit(OpCodes.Ret);
return getterBuilder;
}
private MethodBuilder BuildSetter(TypeBuilder tb, PropertyInfo property, FieldBuilder fb, MethodAttributes attributes)
{
var propertyType = property.PropertyType;
var setterBuilder = tb.DefineMethod($"set_{property.Name}", attributes, null, new Type[] { propertyType });
var setIl = setterBuilder.GetILGenerator();
var exitSet = setIl.DefineLabel(); // define label to jump in case condition is false
var opInequality = propertyType.GetMethod("op_Inequality", new Type[] { propertyType, propertyType });
if (opInequality != null)
{
setIl.Emit(OpCodes.Ldarg_0);
setIl.Emit(OpCodes.Ldfld, fb);
setIl.Emit(OpCodes.Ldarg_1);
setIl.Emit(OpCodes.Call, opInequality);
setIl.Emit(OpCodes.Brfalse_S, exitSet);
}
else
{
// Use object inequality
setIl.Emit(OpCodes.Ldarg_0);
setIl.Emit(OpCodes.Ldfld, fb);
if (propertyType.IsValueType)
{
setIl.Emit(OpCodes.Box, propertyType);
}
setIl.Emit(OpCodes.Ldarg_1);
if (propertyType.IsValueType)
{
setIl.Emit(OpCodes.Box, propertyType);
}
setIl.Emit(OpCodes.Call, EqualsMethod);
setIl.Emit(OpCodes.Brtrue_S, exitSet);
}
setIl.Emit(OpCodes.Ldarg_0); // load string literal
setIl.Emit(OpCodes.Ldarg_1); // value
if (propertyType.IsValueType)
{
setIl.Emit(OpCodes.Box, propertyType);
}
setIl.Emit(OpCodes.Ldstr, property.Name);
var parentType = tb.BaseType;
if (parentType == null)
throw new Exception($"Interface {tb.Name} should be inherited from \"IDbEntity\". ");
var m = parentType.GetMethod("ValueChanged", BindingFlags.Instance | BindingFlags.NonPublic);
setIl.Emit(OpCodes.Call, m);
setIl.Emit(OpCodes.Ldarg_0); // this
setIl.Emit(OpCodes.Ldarg_1); // value
setIl.Emit(OpCodes.Stfld, fb); // save the new value into _name
setIl.MarkLabel(exitSet); // mark the label
setIl.Emit(OpCodes.Ret); // return
return setterBuilder;
}
код установленных значений
internal delegate T Load<out T>(IDataRecord dataRecord) where T : IDbEntity;
private static readonly MethodInfo getValueMethod = typeof(IDataRecord).GetMethod("get_Item", new Type[] { typeof(int) });
private static readonly MethodInfo isDBNullMethod = typeof(IDataRecord).GetMethod("IsDBNull", new Type[] { typeof(int) });
private Load<T> CreateDynamicMethod<T>(Type insType) where T : IDbEntity
{
var method = new DynamicMethod("DynamicCreate", insType, new[] { typeof(IDataRecord) }, insType, true);
ILGenerator generator = method.GetILGenerator();
LocalBuilder result = generator.DeclareLocal(insType);
generator.Emit(OpCodes.Newobj, insType.GetConstructor(Type.EmptyTypes));
generator.Emit(OpCodes.Stloc, result);
var props = insType.GetProperties( BindingFlags.DeclaredOnly |BindingFlags.Public | BindingFlags.Instance);
for (var i = 0; i < props.Length; i++)
{
var info = props[i];
Label endIfLabel = generator.DefineLabel();
if (info.GetSetMethod() != null)
{
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldc_I4, i);
generator.Emit(OpCodes.Callvirt, isDBNullMethod);
generator.Emit(OpCodes.Brtrue, endIfLabel);
generator.Emit(OpCodes.Ldloc, result);
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldc_I4, i);
generator.Emit(OpCodes.Callvirt, getValueMethod);
generator.Emit(OpCodes.Unbox_Any, info.PropertyType);
generator.Emit(OpCodes.Callvirt, info.GetSetMethod());
generator.MarkLabel(endIfLabel);
}
}
generator.Emit(OpCodes.Ldloc, result);
generator.Emit(OpCodes.Ret);
return (Load<T>)method.CreateDelegate(typeof(Load<T>));
}
обновление 1
экран-защелки
обновление 2
метод
обновление 3
решение