jeudi 28 mars 2019

How to implemataion interface method with permanent body?

Im do object factory for dynamicly creating instance by interface with help of reflection.

My code

public static class ObjectFactory
    {
        private static readonly ConcurrentDictionary<Type, Type> TypeCache = new ConcurrentDictionary<Type, Type>();

        public static T CreateInstance<T>(Type parent = null)
        {
            if (!typeof(T).IsInterface) throw new ArgumentException($"Type {typeof(T).Name} must be an interface.");
            var newType = TypeCache.GetOrAdd(typeof(T), t => BuildType(typeof(T), parent));
            return (T)Activator.CreateInstance(newType);
        }

        private static Type BuildType(Type interfaceType, Type parent = null)
        {
            var assemblyName = new AssemblyName($"DynamicAssembly_{Guid.NewGuid():N}");
            var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
            var moduleBuilder = assemblyBuilder.DefineDynamicModule("DynamicModule");
            var typeName = $"{RemoveInterfacePrefix(interfaceType.Name)}_{Guid.NewGuid():N}";
            var typeBuilder = moduleBuilder.DefineType(typeName, TypeAttributes.Public);

            if (parent != null)
                typeBuilder.SetParent(parent);

            typeBuilder.AddInterfaceImplementation(interfaceType);

            var properties = interfaceType.GetProperties(BindingFlags.Instance | BindingFlags.Public);

            var methods = interfaceType.GetMethods(BindingFlags.Instance | BindingFlags.Public);

            BuildProperties(typeBuilder, properties);
            BuildMethods(typeBuilder, methods);

            return typeBuilder.CreateType();

            string RemoveInterfacePrefix(string name) => Regex.Replace(name, "^I", string.Empty);
        }

        private static void BuildMethods(TypeBuilder typeBuilder, IEnumerable<MethodInfo> methods)
        {
            foreach (var method in methods)
            {
                var paramTypes = method.GetParameters().Select(p => p.ParameterType).ToArray();
                AddMethodDynamically(typeBuilder, method.Name, paramTypes, method.ReturnType, "A");
            }
        }

        private static void BuildProperties(TypeBuilder typeBuilder, IEnumerable<PropertyInfo> properties)
        {
            foreach (var property in properties)
            {
                BuildProperty(typeBuilder, property);
            }
        }

        private static PropertyBuilder BuildProperty(TypeBuilder typeBuilder, PropertyInfo property)
        {
            var fieldName = $"<{property.Name}>k__BackingField";

            var propertyBuilder = typeBuilder.DefineProperty(property.Name, System.Reflection.PropertyAttributes.None, property.PropertyType, Type.EmptyTypes);

            // Build backing-field.
            var fieldBuilder = typeBuilder.DefineField(fieldName, property.PropertyType, FieldAttributes.Private);

            var getSetAttributes = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig | MethodAttributes.Virtual;

            var getterBuilder = BuildGetter(typeBuilder, property, fieldBuilder, getSetAttributes);
            var setterBuilder = BuildSetter(typeBuilder, property, fieldBuilder, getSetAttributes);

            propertyBuilder.SetGetMethod(getterBuilder);
            propertyBuilder.SetSetMethod(setterBuilder);

            return propertyBuilder;
        }

        private static MethodBuilder BuildGetter(TypeBuilder typeBuilder, PropertyInfo property, FieldBuilder fieldBuilder, MethodAttributes attributes)
        {
            var getterBuilder = typeBuilder.DefineMethod($"get_{property.Name}", attributes, property.PropertyType, Type.EmptyTypes);
            var ilGenerator = getterBuilder.GetILGenerator();

            ilGenerator.Emit(OpCodes.Ldarg_0);
            ilGenerator.Emit(OpCodes.Ldfld, fieldBuilder);

            if (property.GetCustomAttribute<NotNullAttribute>() != null)
            {
                // Build null check
                ilGenerator.Emit(OpCodes.Dup);

                var isFieldNull = ilGenerator.DefineLabel();
                ilGenerator.Emit(OpCodes.Brtrue_S, isFieldNull);
                ilGenerator.Emit(OpCodes.Pop);
                ilGenerator.Emit(OpCodes.Ldstr, $"{property.Name} isn't set.");

                var invalidOperationExceptionConstructor = typeof(InvalidOperationException).GetConstructor(new Type[] { typeof(string) });
                ilGenerator.Emit(OpCodes.Newobj, invalidOperationExceptionConstructor);
                ilGenerator.Emit(OpCodes.Throw);

                ilGenerator.MarkLabel(isFieldNull);
            }
            ilGenerator.Emit(OpCodes.Ret);

            return getterBuilder;
        }

        private static void AddMethodDynamically(TypeBuilder myTypeBld,
                                             string mthdName,
                                             Type[] mthdParams,
                                             Type returnType,
                                             string mthdAction)
        {

            MethodBuilder myMthdBld = myTypeBld.DefineMethod(
                                                 mthdName,
                                                 MethodAttributes.Public,
                                                 returnType,
                                                 mthdParams);

            ILGenerator ILout = myMthdBld.GetILGenerator();

            int numParams = mthdParams.Length;

            for (byte x = 0; x < numParams; x++)
            {
                ILout.Emit(OpCodes.Ldarg_S, x);
            }

            if (numParams > 1)
            {
                for (int y = 0; y < (numParams - 1); y++)
                {
                    switch (mthdAction)
                    {
                        case "A":
                            ILout.Emit(OpCodes.Add);
                            break;
                        case "M":
                            ILout.Emit(OpCodes.Mul);
                            break;
                        default:
                            ILout.Emit(OpCodes.Add);
                            break;
                    }
                }
            }
            ILout.Emit(OpCodes.Ret);
        }

        private static MethodBuilder BuildSetter(TypeBuilder typeBuilder, PropertyInfo property, FieldBuilder fieldBuilder, MethodAttributes attributes)
        {
            var setterBuilder = typeBuilder.DefineMethod($"set_{property.Name}", attributes, null, new Type[] { property.PropertyType });
            var ilGenerator = setterBuilder.GetILGenerator();

            ilGenerator.Emit(OpCodes.Ldarg_0);
            ilGenerator.Emit(OpCodes.Ldarg_1);

            // Build null check

            if (property.GetCustomAttribute<NotNullAttribute>() != null)
            {
                var isValueNull = ilGenerator.DefineLabel();

                ilGenerator.Emit(OpCodes.Dup);
                ilGenerator.Emit(OpCodes.Brtrue_S, isValueNull);
                ilGenerator.Emit(OpCodes.Pop);
                ilGenerator.Emit(OpCodes.Ldstr, property.Name);

                var argumentNullExceptionConstructor = typeof(ArgumentNullException).GetConstructor(new Type[] { typeof(string) });
                ilGenerator.Emit(OpCodes.Newobj, argumentNullExceptionConstructor);
                ilGenerator.Emit(OpCodes.Throw);

                ilGenerator.MarkLabel(isValueNull);
            }
            ilGenerator.Emit(OpCodes.Stfld, fieldBuilder);
            ilGenerator.Emit(OpCodes.Ret);

            return setterBuilder;
        }
    }

Method BuildMethods not create implementation method by interface. Please help solve 2 problems: 1) create implementation method by interface. 2) all created classes have parent class and all methods on interface after implementaion have only execute method by parent class/ for example

public class Parent
{
   public T Get<T, Y>(string url, params object[] query) => httpClient.Get<T>(url, query);
   public T Post<T, Y>(string url, params Y model) => httpClient.Post<T>(url, model);
}

create intreface

public inteface IRest
{
   [HttpGet("/api/user-ids")] List<string> GetUserIds(int count, int skip);
}

what i want in finally on create instance by interface, i got instance where method GetUserIds execute Get<T, Y> from parent class

var rest = ObjectFactory.CreateInstance<IRest>(typeof(Parent));
var userIds = rest.GetUserIds(10, 0);





Aucun commentaire:

Enregistrer un commentaire