samedi 20 novembre 2021

Generate interface implementation with Reflection.Emit for List of given properties

I am using code from this question to generate class from list of property

public class Field
{
    public string FieldName;
    public Type FieldType;
}

public static class MyTypeBuilder
    {
        public static Type CompileResultType()
        {
            TypeBuilder tb = GetTypeBuilder();
            ConstructorBuilder constructor = tb.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);

            // NOTE: assuming your list contains Field objects with fields FieldName(string) and FieldType(Type)
            foreach (var field in yourListOfFields)
                CreateProperty(tb, field.FieldName, field.FieldType);

            Type objectType = tb.CreateType();
            return objectType;
        }

        private static TypeBuilder GetTypeBuilder()
        {
            var typeSignature = "MyDynamicType";
            var an = new AssemblyName(typeSignature);
            AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
            ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
            TypeBuilder tb = moduleBuilder.DefineType(typeSignature,
                    TypeAttributes.Public |
                    TypeAttributes.Class |
                    TypeAttributes.AutoClass |
                    TypeAttributes.AnsiClass |
                    TypeAttributes.BeforeFieldInit |
                    TypeAttributes.AutoLayout,
                    null);
            return tb;
        }

        private static void CreateProperty(TypeBuilder tb, string propertyName, Type propertyType)
        {
            FieldBuilder fieldBuilder = tb.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);

            PropertyBuilder propertyBuilder = tb.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);
            MethodBuilder getPropMthdBldr = tb.DefineMethod("get_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, propertyType, Type.EmptyTypes);
            ILGenerator getIl = getPropMthdBldr.GetILGenerator();

            getIl.Emit(OpCodes.Ldarg_0);
            getIl.Emit(OpCodes.Ldfld, fieldBuilder);
            getIl.Emit(OpCodes.Ret);

            MethodBuilder setPropMthdBldr =
                tb.DefineMethod("set_" + propertyName,
                  MethodAttributes.Public |
                  MethodAttributes.SpecialName |
                  MethodAttributes.HideBySig,
                  null, new[] { propertyType });

            ILGenerator setIl = setPropMthdBldr.GetILGenerator();
            Label modifyProperty = setIl.DefineLabel();
            Label exitSet = setIl.DefineLabel();

            setIl.MarkLabel(modifyProperty);
            setIl.Emit(OpCodes.Ldarg_0);
            setIl.Emit(OpCodes.Ldarg_1);
            setIl.Emit(OpCodes.Stfld, fieldBuilder);

            setIl.Emit(OpCodes.Nop);
            setIl.MarkLabel(exitSet);
            setIl.Emit(OpCodes.Ret);

            propertyBuilder.SetGetMethod(getPropMthdBldr);
            propertyBuilder.SetSetMethod(setPropMthdBldr);
        }
    }

and I have interface to get/set it's properties to avoid using reflection and dynamic

public interface IDynamicObject
{
    T GetProperty<T>(string propertyName);
    void SetProperty(string propertyName, object value);
}

Can anybody help me modify this code to generate a class implementing my IDynamicObject interface so it generate something like this (for example two string properties "Str1" and "Str2")? Sadly am not good with Reflection.Emit yet...

public class TestClass : IDynamicObject
{
    public string Str1 { get; set; }
    public string Str2 { get; set; }

    public T GetProperty<T>(string propertyName)
    {
        switch (propertyName)
        {
            case nameof(Str1):
                return CastObject<T>(Str1);

            case nameof(Str2):
                return CastObject<T>(Str2);

            default: throw new ArgumentException();
        }
        
    }

    public void SetProperty(string propertyName, object value)
    {
        switch (propertyName)
        {
            case nameof(Str1):
                Str1 = (string)value;
                break;

            case nameof(Str2):
                Str2 = (string)value;
                break;

            default: throw new ArgumentException();
        }
    }

    public T CastObject<T>(object input)
    {
        return (T)input;
    }
}




Aucun commentaire:

Enregistrer un commentaire