как переопределить сеттеры при реализации интерфейса со свойствами, используя TypeBuilder C# - PullRequest
1 голос
/ 15 апреля 2020

То, что я делаю, - это создание динамического c типа из System.Data.DataTable посредством отражения. Но я застрял при реализации интерфейса для моего динамического c типа, созданного TypeBuilder .

Мой интерфейс выглядит следующим образом

public interface ICustomViewCols
{
    int? CardId { get; set; }

    DateTime? OutDate { get; set; }

    string HospCode { get; set; }
}

И мой TypeBuilder выглядит как

var aName = new AssemblyName("UniWeb.CustomView");
var ab =
    AssemblyBuilder.DefineDynamicAssembly(aName,
        AssemblyBuilderAccess.RunAndCollect);
var mb =
    ab.DefineDynamicModule(aName.Name + ".dll");

var tb = mb.DefineType(
    "CustomViewModel",
    TypeAttributes.Public);

tb.AddInterfaceImplementation(typeof(ICustomViewCols));

foreach (var obj in cols)
{
    var col = obj as DataColumn;
    var propType = GetNullableDataType(col.DataType);
    var propName = col.ColumnName;

    if (propName == "HospCode" || propName == "CardId" || propName == "OutDate")
    {
        var getMethod = typeof(ICustomViewCols).GetProperty(propName).GetGetMethod();
        var setMethod = typeof(ICustomViewCols).GetProperty(propName).GetSetMethod();

        var currGetPropMthdBldr = tb.DefineMethod($"get_{propName}",
            MethodAttributes.Public | MethodAttributes.Virtual,
            propType, Type.EmptyTypes);
        var getIl = currGetPropMthdBldr.GetILGenerator();
        getIl.Emit(OpCodes.Ret);
        tb.DefineMethodOverride(currGetPropMthdBldr, getMethod);

        var currSetPropMthdBldr = tb.DefineMethod($"set_{propName}",
            MethodAttributes.Public | MethodAttributes.Virtual,
            null, new[] { propType });
        var setIl = currGetPropMthdBldr.GetILGenerator();
        setIl.Emit(OpCodes.Ldarg_0);
        setIl.Emit(OpCodes.Ldc_I4_1);
        setIl.Emit(OpCodes.Ldarg_1);
        setIl.Emit(OpCodes.Callvirt, setMethod);

        setIl.Emit(OpCodes.Ret);
        tb.DefineMethodOverride(currSetPropMthdBldr, setMethod);
    }
    else
    {
        var field = tb.DefineField(propName, propType, FieldAttributes.Public);
        var property = tb.DefineProperty(propName, PropertyAttributes.None, propType,
            new Type[] {propType});
        var GetSetAttr = MethodAttributes.Public | MethodAttributes.HideBySig;
        var currGetPropMthdBldr =
            tb.DefineMethod($"get_{propName}", GetSetAttr, propType, new Type[] {propType}); // Type.EmptyTypes);
        var currGetIL = currGetPropMthdBldr.GetILGenerator();
        currGetIL.Emit(OpCodes.Ldarg_0);
        currGetIL.Emit(OpCodes.Ldfld, field);
        currGetIL.Emit(OpCodes.Ret);

        var currSetPropMthdBldr =
            tb.DefineMethod($"set_{propName}", GetSetAttr, null, new Type[] {propType});
        var currSetIL = currSetPropMthdBldr.GetILGenerator();
        currSetIL.Emit(OpCodes.Ldarg_0);
        currSetIL.Emit(OpCodes.Ldarg_1);
        currSetIL.Emit(OpCodes.Stfld, field);
        currSetIL.Emit(OpCodes.Ret);

        property.SetGetMethod(currGetPropMthdBldr);
        property.SetSetMethod(currSetPropMthdBldr);
    }
}
var customViewType = tb.CreateType();

И это работает только для геттеров в моем интерфейсе, но не работает для сеттеров. Он выдает исключение System.InvalidOperationException: у метода 'set_HospCode' нет тела метода. Может кто-нибудь мне помочь?

1 Ответ

0 голосов
/ 21 апреля 2020

Вот ответ на мой вопрос.

Я изменил свой интерфейс только с получателями.

public interface ICustomViewCols
{
    int Id { get; }

    DateTime? OutDate { get; }

    string HospCode { get; }
}


А затем я просто реализую получатели интерфейса и рассматриваю установщики как установщики свойств.

    private Type CreateDynamicTypeFromDataColumn(IEnumerable<DataColumn> cols)
    {
        //naming
        var aName = new AssemblyName("UniWeb.CustomView");
        var ab =
            AssemblyBuilder.DefineDynamicAssembly(aName,
                AssemblyBuilderAccess.RunAndCollect);
        var mb =
            ab.DefineDynamicModule(aName.Name + ".dll");

        var tb = mb.DefineType(
            "CustomViewModel",
            TypeAttributes.Public);

        tb.AddInterfaceImplementation(typeof(ICustomViewCols));

        foreach (var col in cols)
        {
            var propName = col.ColumnName;
            var propType = propName == nameof(ICustomViewCols.Id)
                ? col.DataType
                : GetNullableDataType(col.DataType);

            var field = tb.DefineField(propName, propType, FieldAttributes.Public);
            var property = tb.DefineProperty(propName, PropertyAttributes.None, propType,
                new Type[] {propType});
            var propertyAttr = MethodAttributes.Public | MethodAttributes.HideBySig;

            //implement interface getter
            if (propName == nameof(ICustomViewCols.HospCode)
                || propName == nameof(ICustomViewCols.Id)
                || propName == nameof(ICustomViewCols.OutDate))
            {
                var getMethod = typeof(ICustomViewCols).GetProperty(propName).GetGetMethod();

                var currGetPropMthdBldr = tb.DefineMethod($"get_{propName}",
                    MethodAttributes.Public | MethodAttributes.Virtual,
                    propType, Type.EmptyTypes);
                var getIl = currGetPropMthdBldr.GetILGenerator();
                getIl.Emit(OpCodes.Ret);
                tb.DefineMethodOverride(currGetPropMthdBldr, getMethod);
            }

            //set property getter
            else
            {
                var currGetPropMthdBldr =
                    tb.DefineMethod($"get_{propName}", propertyAttr, propType,
                        new Type[] {propType});
                var currGetIL = currGetPropMthdBldr.GetILGenerator();
                currGetIL.Emit(OpCodes.Ldarg_0);
                currGetIL.Emit(OpCodes.Ldfld, field);
                currGetIL.Emit(OpCodes.Ret);
                property.SetGetMethod(currGetPropMthdBldr);
            }

            //set property setter
            var currSetPropMthdBldr =
                tb.DefineMethod($"set_{propName}", propertyAttr, null, new Type[] {propType});
            var currSetIL = currSetPropMthdBldr.GetILGenerator();
            currSetIL.Emit(OpCodes.Ldarg_0);
            currSetIL.Emit(OpCodes.Ldarg_1);
            currSetIL.Emit(OpCodes.Stfld, field);
            currSetIL.Emit(OpCodes.Ret);

            property.SetSetMethod(currSetPropMthdBldr);
        }

        return tb.CreateType();
    }

И это работает.

...