public static Type GetResponseTypeFromMethod(this MethodInfo method, ClientMethodType methodType)
        {
            switch (methodType)
            {
            case ClientMethodType.UnaryCall:
            {
                var type = method.ReturnType.GenericTypeArguments[0];

                return(type);
            }

            case ClientMethodType.ClientStreaming:
            {
                var type = method.ReturnType.GenericTypeArguments[1];

                return(type);
            }

            case ClientMethodType.ServerStreaming:
            {
                var type = method.ReturnType.GenericTypeArguments[0];

                return(type);
            }

            default:
            {
                var type = method.ReturnType.GenericTypeArguments[1];

                return(type);
            }
            }
        }
        public static string GetNormalizedName(this ClientMethodType methodType)
        {
            switch (methodType)
            {
            case ClientMethodType.UnaryCall:
            {
                return("UNARY CALL");
            }

            case ClientMethodType.ClientStreaming:
            {
                return("CLIENT STREAMING");
            }

            case ClientMethodType.ServerStreaming:
            {
                return("SERVER STREAMING");
            }

            default:
            {
                return("DUPLEX STREAMING");
            }
            }
        }
        public static MethodBuilder ImplementProcessingMethod(
            this MethodBuilder method,
            PropertyInfo clientProperty,
            PropertyInfo requestProperty,
            FieldInfo cancellationTokenSourceField,
            PropertyInfo optionsProperty,
            PropertyInfo responseProperty,
            ClientMethodType methodType,
            MethodInfo clientRequestMethod,
            MethodInfo setResponseMethod,
            Type requestType,
            Type responseType,
            PropertyInfo metadataProviderProperty,
            MethodInfo clearResponseMethod)
        {
            var generator = method.GetILGenerator();

            generator.Emit(OpCodes.Ldarg_0);
            generator.Emit(OpCodes.Call, clearResponseMethod);
            generator.Emit(OpCodes.Ldarg_0);
            generator.Emit(OpCodes.Call, clientProperty.GetGetMethod());
            generator.Emit(OpCodes.Dup);
            generator.Emit(OpCodes.Ldvirtftn, clientRequestMethod);

            switch (methodType)
            {
            case ClientMethodType.UnaryCall:
            {
                generator.Emit(OpCodes.Newobj, UnaryCallFuncConstructorMethod(requestType, responseType));
                break;
            }

            case ClientMethodType.ClientStreaming:
            {
                generator.Emit(OpCodes.Newobj, ClientStreamingFuncConstructorMethod(requestType, responseType));
                break;
            }

            case ClientMethodType.ServerStreaming:
            {
                generator.Emit(OpCodes.Newobj, ServerStreamingFuncConstructorMethod(requestType, responseType));
                break;
            }

            default:
            {
                generator.Emit(OpCodes.Newobj, DuplexStreamingFuncConstructorMethod(requestType, responseType));
                break;
            }
            }

            generator.Emit(OpCodes.Ldarg_0);
            generator.Emit(OpCodes.Call, requestProperty.GetGetMethod());
            generator.Emit(OpCodes.Ldarg_0);
            generator.Emit(OpCodes.Newobj, CancellationTokenSourceConstructorMethod);
            generator.Emit(OpCodes.Stfld, cancellationTokenSourceField);
            generator.Emit(OpCodes.Ldarg_0);
            generator.Emit(OpCodes.Ldfld, cancellationTokenSourceField);
            generator.Emit(OpCodes.Ldarg_0);
            generator.Emit(OpCodes.Call, optionsProperty.GetGetMethod());

            if (methodType == ClientMethodType.ServerStreaming || methodType == ClientMethodType.DuplexStreaming)
            {
                generator.Emit(OpCodes.Ldarg_0);
                generator.Emit(OpCodes.Ldftn, responseProperty.GetGetMethod());
                generator.Emit(OpCodes.Newobj, FuncFromStringConstructorMethod);
            }

            generator.Emit(OpCodes.Ldarg_0);
            generator.Emit(OpCodes.Ldftn, setResponseMethod);
            generator.Emit(OpCodes.Newobj, FuncConstructorMethod);
            generator.Emit(OpCodes.Ldarg_0);
            generator.Emit(OpCodes.Call, metadataProviderProperty.GetGetMethod());

            switch (methodType)
            {
            case ClientMethodType.UnaryCall:
            {
                generator.EmitCall(OpCodes.Call, UtilsSendUnaryCallMethod(requestType, responseType), null);
                break;
            }

            case ClientMethodType.ClientStreaming:
            {
                generator.EmitCall(OpCodes.Call, UtilsSendClientStreamingMethod(requestType, responseType), null);
                break;
            }

            case ClientMethodType.ServerStreaming:
            {
                generator.EmitCall(OpCodes.Call, UtilsSendServerStreamingMethod(requestType, responseType), null);
                break;
            }

            default:
            {
                generator.EmitCall(OpCodes.Call, UtilsSendDuplexStreamingMethod(requestType, responseType), null);
                break;
            }
            }

            generator.Emit(OpCodes.Ret);

            return(method);
        }