通过界面和设置值动态创建对象

时间:2019-03-25 08:08:41

标签: c# dynamic emit idatareader

目的:从接口动态创建对象并从IDataReader设置值

现在,我可以从接口动态创建对象类型/实例,但是当我从IDataReader设置值时,会出现错误:

  

“指定的演员表无效”

我用Google搜索了它,但没有太多帮助。我知道错误与发出代码有关,但是我不知道如何修改它。

有关界面中动态创建类型的代码

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 :(得分:0)

目前,该代码正在运行。参见更新3。

1st。删除generator.Emit(OpCodes.Unbox_Any, propertyInfo.PropertyType);不需要。

看图片。

enter image description here

第二。为“ IDataRecord”声明静态methodinfos并将其存储在ConcurrentDictionary中。获取当前的属性调用方法。