Example #1
0
        /// <summary>
        /// Finds the duck implementation for a given interface property.
        /// </summary>
        /// <param name="interfaceProperty">Interface property to find the implementation of.</param>
        /// <returns>If the type given in the constructor implements the given interface property, such property; otherwise, null.</returns>
        private PropertyInfo FindDuckProperty(PropertyInfo interfaceProperty)
        {
            PropertyInfo duckProperty = null;

            PropertyInfo[] properties = m_DuckType.GetProperties();
            foreach (PropertyInfo property in properties)
            {
                // Must have the same name
                if (property.Name == interfaceProperty.Name)
                {
                    // Must have a compatible property type
                    if (DuckTyping.AreTypesCompatible(interfaceProperty.PropertyType, property.PropertyType))
                    {
                        // Must implement the capabilities of the interface property
                        if ((property.CanRead || !interfaceProperty.CanRead) && (property.CanWrite || !interfaceProperty.CanWrite) &&
                            (!interfaceProperty.CanRead || property.GetGetMethod().IsPublic) && (!interfaceProperty.CanWrite || property.GetSetMethod().IsPublic))
                        {
                            // Must have a compatible index parameter list
                            ParameterInfo[] indexParameters = property.GetIndexParameters();
                            ParameterInfo[] interfacePropertyIndexParameters = interfaceProperty.GetIndexParameters();

                            if (indexParameters.Length == interfacePropertyIndexParameters.Length)
                            {
                                bool areParameterTypesCompatible = true;

                                for (int i = 0; i < indexParameters.Length; i++)
                                {
                                    if (!DuckTyping.AreTypesCompatible(interfacePropertyIndexParameters[i].ParameterType, indexParameters[i].ParameterType))
                                    {
                                        areParameterTypesCompatible = false;
                                        break;
                                    }
                                }

                                if (areParameterTypesCompatible)
                                {
                                    duckProperty = property;
                                    break;
                                }
                            }
                        }
                    }
                }
            }

            return(duckProperty);
        }
Example #2
0
        /// <summary>
        /// Implements an interface method in a duck proxy type using a given type builder.
        /// If successful, the implemented method will be added to the given proxy member dictionary.
        /// </summary>
        /// <param name="proxyType">Type builder for the duck proxy type.</param>
        /// <param name="proxyMembers">Dictionary of members of the proxy type.</param>
        /// <param name="duckField">Field that holds a reference to the duck object to forward calls to.</param>
        /// <param name="interfaceMethod">The interface method to implement.</param>
        private void ImplementMethod(TypeBuilder proxyType, ProxyMemberDictionary proxyMembers, FieldInfo duckField, MethodInfo interfaceMethod)
        {
            MethodInfo duckMethod = FindDuckMethod(interfaceMethod);

            if (duckMethod == null)
            {
                throw new NotImplementedException("Duck type does not implement a method named \"" + interfaceMethod.Name + "\" with the same parameters and return type.");
            }

            ParameterInfo[] interfaceMethodParameters = interfaceMethod.GetParameters();
            ParameterInfo[] duckMethodParameters      = duckMethod.GetParameters();

            MethodBuilder proxyMethod   = proxyType.DefineMethod(interfaceMethod.Name, MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final, CallingConventions.HasThis, interfaceMethod.ReturnType, GetParameterTypes(interfaceMethodParameters));
            ILGenerator   proxyMethodIL = proxyMethod.GetILGenerator();

            // Emit IL to load the proxy instance, then load the value of its duck field
            proxyMethodIL.Emit(OpCodes.Ldarg_0);
            proxyMethodIL.Emit(OpCodes.Ldfld, duckField);

            // Emit IL to load method arguments
            for (int i = 0; i < interfaceMethodParameters.Length; i++)
            {
                // Emit IL to load the argument
                proxyMethodIL.Emit(OpCodes.Ldarg, i + 1);

                // Emit IL to cast the argument if necessary
                DuckTyping.EmitCastIL(proxyMethodIL, duckMethodParameters[i].ParameterType, interfaceMethodParameters[i].ParameterType);
            }

            // Emit IL to call the method
            proxyMethodIL.Emit(OpCodes.Callvirt, duckMethod);

            // If we are returning something...
            if (duckMethod.ReturnType != typeof(void))
            {
                // Emit IL to cast the return value if necessary
                DuckTyping.EmitCastIL(proxyMethodIL, interfaceMethod.ReturnType, duckMethod.ReturnType);
            }

            // Emit IL to return.
            proxyMethodIL.Emit(OpCodes.Ret);

            // Add proxy method to proxy member dictionary
            // (This is so that any associated properties or events can refer to it later)
            proxyMembers.Add(duckMethod, proxyMethod);
        }
Example #3
0
        /// <summary>
        /// Finds the duck implementation for a given interface method.
        /// </summary>
        /// <param name="interfaceMethod">Interface method to find the implementation of.</param>
        /// <returns>If the type given in the constructor implements the given interface method, such method; otherwise, null.</returns>
        private MethodInfo FindDuckMethod(MethodInfo interfaceMethod)
        {
            MethodInfo duckMethod = null;

            MethodInfo[] methods = m_DuckType.GetMethods();
            foreach (MethodInfo method in methods)
            {
                // Must be public and have the same name
                if (method.IsPublic && method.Name == interfaceMethod.Name)
                {
                    // Must have a compatible parameter list
                    ParameterInfo[] parameters = method.GetParameters();
                    ParameterInfo[] interfaceMethodParameters = interfaceMethod.GetParameters();

                    if (parameters.Length == interfaceMethodParameters.Length)
                    {
                        bool areParameterTypesCompatible = true;

                        for (int i = 0; i < parameters.Length; i++)
                        {
                            if (!DuckTyping.AreTypesCompatible(interfaceMethodParameters[i].ParameterType, parameters[i].ParameterType))
                            {
                                areParameterTypesCompatible = false;
                                break;
                            }
                        }

                        if (areParameterTypesCompatible)
                        {
                            // Must have a compatible return type
                            if (DuckTyping.AreTypesCompatible(interfaceMethod.ReturnType, method.ReturnType))
                            {
                                duckMethod = method;
                                break;
                            }
                        }
                    }
                }
            }

            return(duckMethod);
        }
Example #4
0
        /// <summary>
        /// Finds the duck implementation for a given interface event.
        /// </summary>
        /// <param name="interfaceEvent">Interface event to find the implementation of.</param>
        /// <returns>If the type given in the constructor implements the given interface event, such event; otherwise, null.</returns>
        private EventInfo FindDuckEvent(EventInfo interfaceEvent)
        {
            EventInfo duckEvent = null;

            EventInfo[] events = m_DuckType.GetEvents();
            foreach (EventInfo _event in events)
            {
                // Must have the same name
                if (_event.Name == interfaceEvent.Name)
                {
                    // Must have a compatible event handler type
                    if (DuckTyping.AreTypesCompatible(interfaceEvent.EventHandlerType, duckEvent.EventHandlerType))
                    {
                        duckEvent = _event;
                        break;
                    }
                }
            }

            return(duckEvent);
        }
Example #5
0
        /// <summary>
        /// Implements the method of the proxy type that invokes the delegate given in its constructor.
        /// </summary>
        /// <param name="proxyType">Type builder to define the invoke method in.</param>
        /// <param name="duckDelegateField">Field that stores a reference to the delegate to call.</param>
        /// <returns>The implemented invoke method.</returns>
        private MethodBuilder ImplementInvokeMethod(TypeBuilder proxyType, FieldInfo duckDelegateField)
        {
            MethodInfo variantMethod = m_ToDelegateType.GetMethod("Invoke");
            MethodInfo duckMethod    = m_FromDelegateType.GetMethod("Invoke");

            ParameterInfo[] variantMethodParameters = variantMethod.GetParameters();
            ParameterInfo[] duckMethodParameters    = duckMethod.GetParameters();

            MethodBuilder proxyMethod   = proxyType.DefineMethod("Invoke", MethodAttributes.Public | MethodAttributes.HideBySig, CallingConventions.HasThis, variantMethod.ReturnType, GetParameterTypes(variantMethodParameters));
            ILGenerator   proxyMethodIL = proxyMethod.GetILGenerator();

            // Emit IL to load the proxy instance, then load the value of its duck delegate field
            proxyMethodIL.Emit(OpCodes.Ldarg_0);
            proxyMethodIL.Emit(OpCodes.Ldfld, duckDelegateField);

            // Emit IL to load method arguments
            for (int i = 0; i < variantMethodParameters.Length; i++)
            {
                // Emit IL to load the argument
                proxyMethodIL.Emit(OpCodes.Ldarg, i + 1);

                // Emit IL to cast the argument if necessary
                DuckTyping.EmitCastIL(proxyMethodIL, duckMethodParameters[i].ParameterType, variantMethodParameters[i].ParameterType);
            }

            // Emit IL to call the delegate
            proxyMethodIL.Emit(OpCodes.Callvirt, duckMethod);

            // If we are returning something...
            if (duckMethod.ReturnType != typeof(void))
            {
                // Emit IL to cast the return value if necessary
                DuckTyping.EmitCastIL(proxyMethodIL, variantMethod.ReturnType, duckMethod.ReturnType);
            }

            // Emit IL to return.
            proxyMethodIL.Emit(OpCodes.Ret);

            return(proxyMethod);
        }
Example #6
0
        /// <summary>
        /// Determines whether a proxy can be generated for the types given in the constructor.
        /// </summary>
        /// <returns>If a proxy can be generated for the types given in the constructor, true; otherwise, false.</returns>
        public bool CanProxy()
        {
            bool canProxy = false;

            if (m_ToDelegateType.IsSubclassOf(typeof(Delegate)) && m_FromDelegateType.IsSubclassOf(typeof(Delegate)))
            {
                MethodInfo variantMethod = m_ToDelegateType.GetMethod("Invoke");
                MethodInfo duckMethod    = m_FromDelegateType.GetMethod("Invoke");

                // Must have a compatible parameter list
                ParameterInfo[] variantMethodParameters = variantMethod.GetParameters();
                ParameterInfo[] duckMethodParameters    = duckMethod.GetParameters();

                if (duckMethodParameters.Length == variantMethodParameters.Length)
                {
                    bool areParameterTypesCompatible = true;

                    for (int i = 0; i < duckMethodParameters.Length; i++)
                    {
                        if (!DuckTyping.AreTypesCompatible(variantMethodParameters[i].ParameterType, duckMethodParameters[i].ParameterType))
                        {
                            areParameterTypesCompatible = false;
                            break;
                        }
                    }

                    if (areParameterTypesCompatible)
                    {
                        // Must have a compatible return type
                        if (DuckTyping.AreTypesCompatible(variantMethod.ReturnType, duckMethod.ReturnType))
                        {
                            canProxy = true;
                        }
                    }
                }
            }

            return(canProxy);
        }
Example #7
0
        /// <summary>
        /// Finds the duck implementation for a given interface method.
        /// </summary>
        /// <param name="interfaceMethod">Interface method to find the implementation of.</param>
        /// <returns>If the type given in the constructor implements the given interface method, such method; otherwise, null.</returns>
        private MethodInfo FindDuckMethod(MethodInfo interfaceMethod)
        {
            MethodInfo duckMethod = null;
            int        bestParameterCompatibilityScore = int.MinValue;

            MethodInfo[] methods = m_DuckType.GetMethods();
            foreach (MethodInfo method in methods)
            {
                // If casting a static class, must be static; if not, must not be static
                if (method.IsStatic == m_IsDuckStatic)
                {
                    // Must be public and have the same name
                    if (method.IsPublic && method.Name == interfaceMethod.Name)
                    {
                        // Must both be non-generic or both generic
                        if (method.IsGenericMethodDefinition == interfaceMethod.IsGenericMethodDefinition)
                        {
                            // Must have compatible generic arguments
                            bool genericArgumentsMatch = false;

                            if (method.IsGenericMethodDefinition)
                            {
                                Type[] genericArguments          = method.GetGenericArguments();
                                Type[] interfaceGenericArguments = interfaceMethod.GetGenericArguments();

                                if (genericArguments.Length == interfaceGenericArguments.Length)
                                {
                                    // TODO: Check generic argument constraints.

                                    genericArgumentsMatch = true;
                                }
                            }
                            else
                            {
                                genericArgumentsMatch = true;
                            }

                            if (genericArgumentsMatch)
                            {
                                // Must have a compatible parameter list
                                int parameterCompatibilityScore = 0;

                                ParameterInfo[] parameters = method.GetParameters();
                                ParameterInfo[] interfaceMethodParameters = interfaceMethod.GetParameters();

                                if (parameters.Length == interfaceMethodParameters.Length)
                                {
                                    bool areParameterTypesCompatible = true;

                                    for (int i = 0; i < parameters.Length; i++)
                                    {
                                        if (!DuckTyping.AreTypesCompatible(parameters[i].ParameterType, interfaceMethodParameters[i].ParameterType, true))
                                        {
                                            areParameterTypesCompatible = false;
                                            break;
                                        }

                                        parameterCompatibilityScore += DuckTyping.GetTypeCompatibilityScore(parameters[i].ParameterType, interfaceMethodParameters[i].ParameterType);
                                    }

                                    if (areParameterTypesCompatible)
                                    {
                                        // Must have a compatible return type
                                        if (DuckTyping.AreTypesCompatible(interfaceMethod.ReturnType, method.ReturnType, true))
                                        {
                                            parameterCompatibilityScore += DuckTyping.GetTypeCompatibilityScore(interfaceMethod.ReturnType, method.ReturnType);

                                            // Since there may be multiple method overloads that are compatible with the
                                            // interface method, we want to choose the best one.
                                            if (parameterCompatibilityScore > bestParameterCompatibilityScore)
                                            {
                                                duckMethod = method;
                                                bestParameterCompatibilityScore = parameterCompatibilityScore;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }

            return(duckMethod);
        }
Example #8
0
        /// <summary>
        /// Implements an interface event in a duck proxy type using a given type builder.
        /// If successful, the implemented event will be added to the given proxy member dictionary.
        /// </summary>
        /// <param name="proxyType">Type builder for the duck proxy type.</param>
        /// <param name="proxyMembers">Dictionary of members of the proxy type.</param>
        /// <param name="duckField">Field that holds a reference to the duck object to forward calls to.</param>
        /// <param name="constructorIL">IL generator to use to add code to the constructor if necessary.</param>
        /// <param name="interfaceEvent">The interface event to implement.</param>
        private void ImplementEvent(TypeBuilder proxyType, ProxyMemberDictionary proxyMembers, FieldInfo duckField, ILGenerator constructorIL, EventInfo interfaceEvent)
        {
            EventInfo duckEvent = FindDuckEvent(interfaceEvent);

            if (duckEvent == null)
            {
                throw new NotImplementedException("Duck type does not implement an event named \"" + interfaceEvent.Name + "\" with the same event handler type.");
            }

            EventBuilder proxyEvent = proxyType.DefineEvent(interfaceEvent.Name, EventAttributes.None, interfaceEvent.EventHandlerType);

            Type interfaceEventHandlerType = interfaceEvent.EventHandlerType;
            Type duckEventHandlerType      = duckEvent.EventHandlerType;

            // Define event add method
            MethodBuilder addMethod   = proxyType.DefineMethod("add_" + interfaceEvent.Name, MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final, CallingConventions.HasThis, typeof(void), new Type[] { interfaceEventHandlerType });
            ILGenerator   addMethodIL = addMethod.GetILGenerator();

            // Define event remove method
            MethodBuilder removeMethod   = proxyType.DefineMethod("remove_" + interfaceEvent.Name, MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final, CallingConventions.HasThis, typeof(void), new Type[] { interfaceEventHandlerType });
            ILGenerator   removeMethodIL = removeMethod.GetILGenerator();

            if (interfaceEventHandlerType == duckEventHandlerType)
            {
                // If the event handler types are the same, we can just forward calls to add and remove like normal.

                MethodInfo duckAddMethod = duckEvent.GetAddMethod();
                if (!duckAddMethod.IsStatic)
                {
                    addMethodIL.Emit(OpCodes.Ldarg_0);
                    addMethodIL.Emit(OpCodes.Ldfld, duckField);
                    addMethodIL.Emit(OpCodes.Ldarg_1);
                    addMethodIL.Emit(OpCodes.Callvirt, duckAddMethod);
                }
                else
                {
                    addMethodIL.Emit(OpCodes.Ldarg_1);
                    addMethodIL.Emit(OpCodes.Call, duckAddMethod);
                }

                MethodInfo duckRemoveMethod = duckEvent.GetRemoveMethod();
                if (!duckRemoveMethod.IsStatic)
                {
                    removeMethodIL.Emit(OpCodes.Ldarg_0);
                    removeMethodIL.Emit(OpCodes.Ldfld, duckField);
                    removeMethodIL.Emit(OpCodes.Ldarg_1);
                    removeMethodIL.Emit(OpCodes.Callvirt, duckRemoveMethod);
                }
                else
                {
                    removeMethodIL.Emit(OpCodes.Ldarg_1);
                    removeMethodIL.Emit(OpCodes.Call, duckRemoveMethod);
                }
            }
            else
            {
                // If the event handler types are different, we need some special behavior.  Specifically, the event handlers
                // added using the interface are kept as a seperate multicast delegate in the proxy class and an event handler
                // is added to the duck that calls the proxy event delegate.

                // Define the event multicast delegate field
                FieldBuilder eventHandlerField = proxyType.DefineField(interfaceEvent.Name, interfaceEventHandlerType, FieldAttributes.Private);

                // Implement the add method
                addMethodIL.Emit(OpCodes.Ldarg_0);
                addMethodIL.Emit(OpCodes.Ldarg_0);
                addMethodIL.Emit(OpCodes.Ldfld, eventHandlerField);
                addMethodIL.Emit(OpCodes.Ldarg_1);
                addMethodIL.Emit(OpCodes.Call, typeof(Delegate).GetMethod("Combine", new Type[] { typeof(Delegate), typeof(Delegate) }));
                addMethodIL.Emit(OpCodes.Castclass, interfaceEventHandlerType);
                addMethodIL.Emit(OpCodes.Stfld, eventHandlerField);

                // Implement the remove method
                removeMethodIL.Emit(OpCodes.Ldarg_0);
                removeMethodIL.Emit(OpCodes.Ldarg_0);
                removeMethodIL.Emit(OpCodes.Ldfld, eventHandlerField);
                removeMethodIL.Emit(OpCodes.Ldarg_1);
                removeMethodIL.Emit(OpCodes.Call, typeof(Delegate).GetMethod("Remove", new Type[] { typeof(Delegate), typeof(Delegate) }));
                removeMethodIL.Emit(OpCodes.Castclass, interfaceEventHandlerType);
                removeMethodIL.Emit(OpCodes.Stfld, eventHandlerField);

                // Set some local variables for later use...
                MethodInfo interfaceInvokeMethod = interfaceEventHandlerType.GetMethod("Invoke");
                Type[]     interfaceInvokeMethodParameterTypes = GetParameterTypes(interfaceInvokeMethod.GetParameters());

                MethodInfo duckInvokeMethod = duckEventHandlerType.GetMethod("Invoke");
                Type[]     duckInvokeMethodParameterTypes = GetParameterTypes(duckInvokeMethod.GetParameters());


                // Define the method that will serve as an event handler to the duck which will invoke the proxy's delegate
                MethodBuilder proxyInvokeMethod   = proxyType.DefineMethod("Invoke" + interfaceEvent.Name, MethodAttributes.Private | MethodAttributes.Virtual | MethodAttributes.Final, CallingConventions.HasThis, duckInvokeMethod.ReturnType, duckInvokeMethodParameterTypes);
                ILGenerator   proxyInvokeMethodIL = proxyInvokeMethod.GetILGenerator();

                // First, check if the proxy event handler is null
                Label ifEventHandlerIsNullLabel = proxyInvokeMethodIL.DefineLabel();
                proxyInvokeMethodIL.Emit(OpCodes.Ldarg_0);
                proxyInvokeMethodIL.Emit(OpCodes.Ldfld, eventHandlerField);
                proxyInvokeMethodIL.Emit(OpCodes.Ldnull);
                proxyInvokeMethodIL.Emit(OpCodes.Ceq);
                proxyInvokeMethodIL.Emit(OpCodes.Brtrue_S, ifEventHandlerIsNullLabel);

                // If the proxy event handler is not null, invoke it
                proxyInvokeMethodIL.Emit(OpCodes.Ldarg_0);
                proxyInvokeMethodIL.Emit(OpCodes.Ldfld, eventHandlerField);
                for (int i = 0; i < interfaceInvokeMethodParameterTypes.Length; i++)
                {
                    proxyInvokeMethodIL.Emit(OpCodes.Ldarg, i + 1);
                    DuckTyping.EmitCastIL(proxyInvokeMethodIL, interfaceInvokeMethodParameterTypes[i], duckInvokeMethodParameterTypes[i]);
                }
                proxyInvokeMethodIL.Emit(OpCodes.Callvirt, interfaceInvokeMethod);

                // If the proxy event handler is null, execution jumps here
                proxyInvokeMethodIL.MarkLabel(ifEventHandlerIsNullLabel);

                // Return
                proxyInvokeMethodIL.Emit(OpCodes.Ret);


                // Add code to the constructor to add the event handler to the duck.
                MethodInfo duckAddMethod = duckEvent.GetAddMethod();
                if (!duckAddMethod.IsStatic)
                {
                    constructorIL.Emit(OpCodes.Ldarg_0);
                    constructorIL.Emit(OpCodes.Ldfld, duckField);
                }
                constructorIL.Emit(OpCodes.Ldarg_0);
                constructorIL.Emit(OpCodes.Ldftn, proxyInvokeMethod);
                constructorIL.Emit(OpCodes.Newobj, duckEventHandlerType.GetConstructor(new Type[] { typeof(object), typeof(IntPtr) }));
                if (!duckAddMethod.IsStatic)
                {
                    constructorIL.Emit(OpCodes.Callvirt, duckAddMethod);
                }
                else
                {
                    constructorIL.Emit(OpCodes.Call, duckAddMethod);
                }
            }

            // Finish add method and set it for the event
            addMethodIL.Emit(OpCodes.Ret);
            proxyEvent.SetAddOnMethod(addMethod);

            // Finish remove method and set it for the event
            removeMethodIL.Emit(OpCodes.Ret);
            proxyEvent.SetRemoveOnMethod(removeMethod);

            // Other associated methods appear before the event in the member array; thus, they have already been
            // defined and implemented.  The proxy member dictionary is used to refer to these.
            MethodInfo raiseMethod = duckEvent.GetRaiseMethod();

            if (raiseMethod != null)
            {
                proxyEvent.SetRaiseMethod((MethodBuilder)(proxyMembers[raiseMethod]));
            }

            MethodInfo[] otherDuckMethods = duckEvent.GetOtherMethods();
            foreach (MethodInfo otherDuckMethod in otherDuckMethods)
            {
                if (proxyMembers.ContainsKey(otherDuckMethod))
                {
                    proxyEvent.AddOtherMethod((MethodBuilder)(proxyMembers[otherDuckMethod]));
                }
            }

            // Add proxy event to the proxy member dictionary
            // (This is not really necessary, but good to keep things consistent)
            // if (proxyMembers != null) proxyMembers[duckEvent] =  proxyEvent;
            // For some reason, EventBuilder does not inherit MemberInfo, so it cannot be added.
        }
Example #9
0
        /// <summary>
        /// Implements an interface method in a duck proxy type using a given type builder.
        /// If successful, the implemented method will be added to the given proxy member dictionary.
        /// </summary>
        /// <param name="proxyType">Type builder for the duck proxy type.</param>
        /// <param name="proxyMembers">Dictionary of members of the proxy type.</param>
        /// <param name="duckField">Field that holds a reference to the duck object to forward calls to.</param>
        /// <param name="constructorIL">IL generator to use to add code to the constructor if necessary.</param>
        /// <param name="interfaceMethod">The interface method to implement.</param>
        private void ImplementMethod(TypeBuilder proxyType, ProxyMemberDictionary proxyMembers, FieldInfo duckField, ILGenerator constructorIL, MethodInfo interfaceMethod)
        {
            MethodInfo duckMethod = FindDuckMethod(interfaceMethod);

            if (duckMethod == null)
            {
                throw new NotImplementedException("Duck type does not implement a method named \"" + interfaceMethod.Name + "\" with compatible parameters and return type.");
            }

            if (!duckMethod.IsSpecialName || (!duckMethod.Name.StartsWith("add_") && !duckMethod.Name.StartsWith("remove_")))
            {
                ParameterInfo[] interfaceMethodParameters = interfaceMethod.GetParameters();
                ParameterInfo[] duckMethodParameters      = duckMethod.GetParameters();

                MethodBuilder proxyMethod = proxyType.DefineMethod(interfaceMethod.Name, MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final, CallingConventions.HasThis, interfaceMethod.ReturnType, GetParameterTypes(interfaceMethodParameters));

                GenericTypeParameterBuilder[] genericParameters;

                if (interfaceMethod.IsGenericMethodDefinition)
                {
                    Type[]   interfaceMethodGenericArguments = interfaceMethod.GetGenericArguments();
                    string[] genericParameterNames           = new string[interfaceMethodGenericArguments.Length];
                    for (int i = 0; i < interfaceMethodGenericArguments.Length; i++)
                    {
                        genericParameterNames[i] = interfaceMethodGenericArguments[i].Name;
                    }
                    genericParameters = proxyMethod.DefineGenericParameters(genericParameterNames);
                }
                else
                {
                    genericParameters = new GenericTypeParameterBuilder[0];
                }

                ILGenerator proxyMethodIL = proxyMethod.GetILGenerator();

                // Emit IL to load the duck object if the method is not static
                if (!duckMethod.IsStatic)
                {
                    // Emit IL to load the proxy instance, then load the value of its duck field
                    proxyMethodIL.Emit(OpCodes.Ldarg_0);
                    proxyMethodIL.Emit(OpCodes.Ldfld, duckField);
                }

                // Emit IL to load method arguments
                for (int i = 0; i < interfaceMethodParameters.Length; i++)
                {
                    // Emit IL to load the argument
                    proxyMethodIL.Emit(OpCodes.Ldarg, 1 + i);

                    // Emit IL to cast the argument if necessary
                    DuckTyping.EmitCastIL(proxyMethodIL, duckMethodParameters[i].ParameterType, interfaceMethodParameters[i].ParameterType);
                }

                MethodInfo methodToCall;
                if (!duckMethod.IsGenericMethodDefinition)
                {
                    methodToCall = duckMethod;
                }
                else
                {
                    methodToCall = duckMethod.MakeGenericMethod((Type[])(genericParameters));
                }

                // Emit IL to call the method
                if (!duckMethod.IsStatic)
                {
                    proxyMethodIL.Emit(OpCodes.Callvirt, methodToCall);
                }
                else
                {
                    proxyMethodIL.Emit(OpCodes.Call, methodToCall);
                }

                // If we are returning something...
                if (duckMethod.ReturnType != typeof(void))
                {
                    // Emit IL to cast the return value if necessary
                    DuckTyping.EmitCastIL(proxyMethodIL, interfaceMethod.ReturnType, duckMethod.ReturnType);
                }

                // Emit IL to return.
                proxyMethodIL.Emit(OpCodes.Ret);

                // Add proxy method to proxy member dictionary
                // (This is so that any associated properties or events can refer to it later)
                if (proxyMembers != null)
                {
                    proxyMembers[duckMethod] = proxyMethod;
                }
            }
        }