Как получить экземпляр типа Reflection.Emit? - PullRequest
1 голос
/ 07 января 2020

Итак, у меня есть класс:

public class MyClass : IMyClass
{
    public string foo {get;}
    public MyClass bar {get;}
}

И интерфейс:

public interface IMyClass
{
    string foo {get;}
}

И система для создания излучаемого типа:

    private static Type MakeDynamicType<T>() where T : class
    {
        var myType = GetTypeBuilder();

        myType.AddInterfaceImplementation(typeof(T));

        var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
        foreach (var property in properties)
            AddProperty(myType, property, typeof(T));

        AddCtor(myType, typeof(T));

        return myType.CreateType();
    }

    private static void AddCtor(TypeBuilder myType, Type inputParamType)
    {
        var myCtor = myType.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, null);
        var ilGenerator = myCtor.GetILGenerator();
        ilGenerator.Emit(OpCodes.Ret);
    }


    private const MethodAttributes GET_SET_ATTR = MethodAttributes.Public | MethodAttributes.SpecialName |
                                                  MethodAttributes.HideBySig | MethodAttributes.Virtual;

    private static void AddProperty(TypeBuilder myType, PropertyInfo property, Type interfaceType)
    {
        var myField = myType.DefineField($"m_{property.Name}", property.PropertyType, FieldAttributes.Private);

        var myProperty = myType.DefineProperty(property.Name, PropertyAttributes.HasDefault, property.PropertyType,
            parameterTypes: null);

        var interfaceGetMethod = interfaceType.GetMethod($"get_{property.Name}");
        if (interfaceGetMethod != null)
            AddGetter(myType, property, myField, myProperty, interfaceGetMethod);

        var interfaceSetMethod = interfaceType.GetMethod($"set_{property.Name}");
        if (interfaceSetMethod != null)
            AddSetter(myType, property, myField, myProperty, interfaceSetMethod);
    }

    private static void AddGetter(TypeBuilder myType, PropertyInfo property, FieldInfo myField,
        PropertyBuilder myProperty, MethodInfo interfaceGetMethod)
    {
        var myGet = myType.DefineMethod($"get_{property.Name}", GET_SET_ATTR, property.PropertyType,
            Type.EmptyTypes);
        var getIl = myGet.GetILGenerator();
        getIl.Emit(OpCodes.Ldarg_0);
        getIl.Emit(OpCodes.Ldfld, myField);
        getIl.Emit(OpCodes.Ret);
        myProperty.SetGetMethod(myGet);
        myType.DefineMethodOverride(myGet, interfaceGetMethod);
    }

    private static void AddSetter(TypeBuilder myType, PropertyInfo property, FieldInfo myField,
        PropertyBuilder myProperty, MethodInfo interfaceSetMethod)
    {
        var mySet = myType.DefineMethod($"set_{property.Name}", GET_SET_ATTR, returnType: null,
            new[] { property.PropertyType });
        var setIl = mySet.GetILGenerator();
        setIl.Emit(OpCodes.Ldarg_0);
        setIl.Emit(OpCodes.Ldarg_1);
        setIl.Emit(OpCodes.Stfld, myField);
        setIl.Emit(OpCodes.Ret);
        myProperty.SetSetMethod(mySet);
        myType.DefineMethodOverride(mySet, interfaceSetMethod);
    }

    private static TypeBuilder GetTypeBuilder()
    {
        var myDomain = Thread.GetDomain();
        var myAsmName = new AssemblyName("MyDynamicAssembly");

        var myAsmBuilder = myDomain.DefineDynamicAssembly(myAsmName, AssemblyBuilderAccess.RunAndSave);
        var myModBuilder = myAsmBuilder.DefineDynamicModule(myAsmName.Name, myAsmName.Name + ".dll");

        return myModBuilder.DefineType("MyDynamicType", TypeAttributes.Public);
    }

Итак Теперь, как мне создать экземпляр моего отраженного типа из ссылки на тип IMyClass для объекта MyClass?

    public static IEnumerable<T> ToInterfacedObjects<T>(this IEnumerable<T> data) where T : class
    {
        var myType = MakeDynamicType<T>();

        var list = new List<T>();
        foreach (var datum in data)
        {
            list.Add((T)myType.GetValue(datum));//What do I write for GetValue??
        }

        return list;
    }

Моя цель - начать с IMyClass, который имеет базовый тип MyClass, который имеет как foo и bar, так и закончить с IMyClass, который имеет базовый тип испускаемый тип, который имеет foo, но не bar.

1 Ответ

1 голос
/ 07 января 2020

Если у вас есть реализация для MakeDynamicType<T> и предполагается, что ваш интерфейс имеет только простые свойства {get; set;}, разве это не будет так просто:

public static IList<T> ToInterfacedObjects<T>(this IEnumerable<T> data) where T : class
{
    var myType = MakeDynamicType<T>();
    var list = new List<T>();
    foreach (var datum in data)
    {
        var obj = (T)Activator.CreateInstance(myType);
        foreach (var pi in typeof(T).GetProperties())
        {
            var val = pi.GetValue(datum);
            pi.SetValue(obj, val);
        }
        list.Add(obj);
    }
    return list;
}
...