динамическое создание объекта из интерфейса и установка значения - PullRequest
0 голосов
/ 25 марта 2019

цель: динамическое создание объекта из интерфейса и установка значений из 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

экран-защелки

enter image description here enter image description here

обновление 2

метод

enter image description here

обновление 3

решение

enter image description here

1 Ответ

0 голосов
/ 26 марта 2019

пока код работает.см. обновление 3.

1-й.удалить generator.Emit(OpCodes.Unbox_Any, propertyInfo.PropertyType); это не нужно.

см. рисунок.

enter image description here

2-й.объявите статические methodinfos для «IDataRecord» и сохраните их в ConcurrentDictionary.получить текущий метод для вызова свойства.

...