如何实现与永久体的接口方法?

时间:2019-03-28 12:09:07

标签: c# .net reflection

我正在使用对象工厂通过接口在反射的帮助下动态创建实例。

我的代码:

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;
    }
}

方法BuildMethods并未按接口创建实现方法。

请帮助解决2个问题:

  1. 通过接口创建实现方法;
  2. 所有创建的类都具有父类,并且实现后接口上的所有方法仅由父类/执行(例如)
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);
}

创建界面:

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

我最终想要通过接口创建实例时,我得到了实例GetUserIds从父类执行Get<T, Y>的实例

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

0 个答案:

没有答案