/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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. }
/// <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; } } }