c# - 方法实现中的正文签名和声明不匹配

标签 c# .net-core cil reflection.emit

我想在运行时实现一个接口(interface),但是当我执行时

return Activator.CreateInstance(typeBuilder.CreateTypeInfo().AsType(), new RPCRequestProxy());

RPCClientFactory.cs 中抛出

System.TypeLoadException: Signature of the body and declaration in a method implementation do not match.

我使用的是 dotnet core 1.0.4。

入口点Program.cs:

namespace RPC
{
    interface IRCPClient
    {
        Task Foo([UrlParam]int b, [UrlParam]string a, [UrlParam] DateTime c);
    }

    class Program
    {
        static void Main(string[] args)
        {
            var factory = new RPCClientFactory();
            factory.Register<IRCPClient>();

            var cli = factory.GetClient<IRCPClient>();
            cli.Foo(1, "HelloWorld", DateTime.Now);

            Console.ReadKey();
        }
    }
}

RPCClientFactory.cs:

    public class RPCClientFactory
    {
        private readonly ConcurrentDictionary<Type, object> _clients = new ConcurrentDictionary<Type, object>();

        public void Register<ClientType>()
        {
            Register(typeof(ClientType));
        }

        public void Register(Type type)
        {
            if (!_clients.TryAdd(type, CreateImplInstance(type)))
                throw new InvalidOperationException($"bad type{type}");
        }

        public object CreateImplInstance(Type type)
        {
            var typeBuilder = CreateTypeBuilder(type);
            var methods = type.GetMethods();

            var field = CreateFiled(typeBuilder);
            CreateCotr(typeBuilder, field);
            CreateMethods(typeBuilder, methods, field);

            typeBuilder.AddInterfaceImplementation(type);

            return Activator.CreateInstance(typeBuilder.CreateTypeInfo().AsType(), new RPCRequestProxy());
        }

        private static TypeBuilder CreateTypeBuilder(Type type)
        {
            var typeSignature = "MyDynamicType";
            var an = new AssemblyName(typeSignature);
            var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(Guid.NewGuid().ToString()), 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 FieldInfo CreateFiled(TypeBuilder typeBuilder)
        {
            return typeBuilder.DefineField("_proxy", typeof(RPCRequestProxy), FieldAttributes.Private);
        }

        private static void CreateCotr(TypeBuilder typeBuilder, FieldInfo proxyField)
        {
            var ctorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public
                , CallingConventions.Standard
                , new[] { typeof(RPCRequestProxy) });

            var il = ctorBuilder.GetILGenerator();

            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Call, typeof(object).GetConstructor(new Type[0]));
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Ldarg_1);
            il.Emit(OpCodes.Stfld, proxyField);
            il.Emit(OpCodes.Ret);
        }

        public void CreateMethods(TypeBuilder typeBuilder, MethodInfo[] methods, FieldInfo field)
        {
            foreach (var method in methods)
            {
                CreateMethod(typeBuilder, method, field);
            }
        }

        private static void CreateMethod(TypeBuilder typeBuilder, MethodInfo method, FieldInfo proxyFiled)
        {
            var paramters = method.GetParameters();

            var methodBuilder = typeBuilder.DefineMethod(method.Name,
                MethodAttributes.Public,
                CallingConventions.Standard,
                method.ReturnType,
                paramters.Select(x => x.GetType()).ToArray());

            var il = methodBuilder.GetILGenerator();

            il.Emit(OpCodes.Newobj, typeof(Dictionary<string, object>).GetConstructor(new Type[0]));
            il.Emit(OpCodes.Stloc_0);

            for (var i = 0; i < paramters.Length; ++i)
            {
                var param = paramters[i];

                if (param.GetCustomAttribute<BodyParamAttribute>() != null) continue;

                il.Emit(OpCodes.Ldloc_0);
                il.Emit(OpCodes.Ldstr, param.Name);
                il.Emit(OpCodes.Ldarg, i + 1);

                if (param.ParameterType.GetTypeInfo().IsValueType)
                {
                    il.Emit(OpCodes.Box, param.GetType());
                }

                il.Emit(OpCodes.Callvirt, typeof(Dictionary<string, object>).GetMethod("Add"));
            }

            var proxyMethod = typeof(RPCRequestProxy)
                .GetMethods()
                .First(x => x.Name == "PostAsync" && x.GetParameters().Length == 2);

            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Ldfld, proxyFiled);
            il.Emit(OpCodes.Ldstr, "xxx.xxx.xxx");
            il.Emit(OpCodes.Ldloc_0);
            il.Emit(OpCodes.Callvirt, proxyMethod);
            il.Emit(OpCodes.Stloc_1);
            il.Emit(OpCodes.Ldloc_1);
            il.Emit(OpCodes.Ret);

            typeBuilder.DefineMethodOverride(methodBuilder, method);
        }

        public ClientType GetClient<ClientType>() where ClientType : class
        {
            _clients.TryGetValue(typeof(ClientType), out object cli);
            return cli as ClientType;
        }

        public ClientType GetRequiredClient<ClientType>() where ClientType : class
        {
            var cli = GetClient<ClientType>();

            if (cli == null) throw new InvalidOperationException($"bad type{typeof(ClientType)}");

            return cli;
        }
    }

RPCRequestProxy.cs:

    public class RPCRequestProxy
    {
        public RPCRequestProxy()
        {

        }

        public async Task PostAsync(string url, IDictionary<string, object> urlParams)
        {
            await Task.Run(() =>
            {
                Console.WriteLine("URL:"  + url);

                Console.WriteLine("QueryString:");
                foreach (var kv in urlParams)
                {
                    Console.WriteLine($"{kv.Key}={kv.Value}");
                }

            });
        }
    }

注册IRCPClient后,希望它生成这样一个类:

    class RCPClient : IRCPClient
    {
        private readonly RPCRequestProxy _proxy;

        public RCPClient(RPCRequestProxy proxy)
        {
            _proxy = proxy;
        }

        public Task Foo([UrlParam] int b, [UrlParam] string a, [UrlParam] DateTime c)
        {
            var urlParams = new Dictionary<string, object>();

            urlParams.Add(nameof(b), b);
            urlParams.Add(nameof(a), a);
            urlParams.Add(nameof(c), c);

            return _proxy.PostAsync("xxx.xxx.xxx", urlParams);
        }
    }

最佳答案

最后,我解决了:

1:我们必须在使用本地值之前DeclareLocal

2:将 paramters.Select(x => x.GetType()).ToArray() 更改为 paramters.Select(x => x.ParameterType).ToArray()

这是 RPCClientFactory.cs

我的最终 CreateMethod 方法
        private static void CreateMethod(TypeBuilder typeBuilder, MethodInfo method, FieldInfo proxyFiled)
        {
            var paramters = method.GetParameters();

            var methodBuilder = typeBuilder.DefineMethod(method.Name,
                MethodAttributes.Public 
                | MethodAttributes.Virtual
                | MethodAttributes.HideBySig
                | MethodAttributes.SpecialName,
                CallingConventions.Standard,
                method.ReturnType,
                paramters.Select(x => x.ParameterType).ToArray()); // change this line

            var il = methodBuilder.GetILGenerator();

            il.DeclareLocal(typeof(Dictionary<string, object>)); // add this line
            il.Emit(OpCodes.Newobj, typeof(Dictionary<string, object>).GetConstructor(new Type[0]));
            il.Emit(OpCodes.Stloc_0);

            for (short i = 0; i < paramters.Length; ++i)
            {
                var param = paramters[i];


                il.Emit(OpCodes.Ldloc_0);
                il.Emit(OpCodes.Ldstr, param.Name);
                il.Emit(OpCodes.Ldarg, (short)(i +1));

                if (param.ParameterType.GetTypeInfo().IsValueType)
                {
                    il.Emit(OpCodes.Box, param.ParameterType);
                }

                il.Emit(OpCodes.Callvirt, typeof(Dictionary<string, object>).GetMethod("Add"));
            }

            var proxyMethod = typeof(RPCRequestProxy)
                .GetMethods()
                .First(x => x.Name == "PostAsync" && x.GetParameters().Length == 2);

            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Ldfld, proxyFiled);
            il.Emit(OpCodes.Ldstr, "xxx.xxx.xxx");
            il.Emit(OpCodes.Ldloc_0);
            il.Emit(OpCodes.Callvirt, proxyMethod);
            il.Emit(OpCodes.Ret);
        }

关于c# - 方法实现中的正文签名和声明不匹配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44993532/

相关文章:

.net - 为什么 MethodBody.GetILAsByteArray 在不同的平台上返回不同的数组?

c# - IL 约束调用

c# - .NET 如何使用具有单独层的 MassTransit saga

c# - 二进制搜索算法随机生成的数组项不起作用

c# - 从 arrayList 中获取值

C# 创建一个比较两个成员属性的 LambdaExpression(对于 EF Core HasQueryFilter)

.net-core - SonarQube dotnet 核心 : how to avoid the duplicate guid error without altering the default build task

c# - 在 C# 中检查图像是否为空白

c# - .NET Core/.NET Standard 2.0 中的 ReliableSession/MaxRetryCount

.net - 为什么 `OpCode.Value` 具有 "wrong"字节序?