Click here to Skip to main content
15,885,366 members
Articles / Desktop Programming / WPF

Templates, Inversion of Control, Factories, and so on

Rate me:
Please Sign up or sign in to vote.
4.75/5 (5 votes)
8 Jul 2011CPOL11 min read 22.9K   270   18  
This article gives a little presentation of Control Templates, Data Templates, Inversion of Control, and Factories, explaining why they are all related and how to better use them.
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.Serialization;
using Pfz.DynamicObjects.Internal;
using Pfz.Extensions;

namespace Pfz.DynamicObjects
{
    // TODO allow a different base type (BaseDuckFor...) that can implement some of the interfaces.
    // this way, instead of checking only for ISerializable, must check for interfaces already implemented.
    // For any interface already implemented, the redirect will not be done.
    // And so, you can create partial implemented classes, that are only finished at run-time.
    // Biggest example: AdvancedDisposable. This way, any "custom" IDisposableObject could be
    // promoted to an IAdvancedDisposable by a class that implement it. But, all other methods
    // will use the methods from the original interface requested (which must only be an IDisposable).
	/// <summary>
	/// Class responsible for creating instances of objects that implement a given interface for an object that
	/// has the needed characteristics but does not actually implement the interface (that is, the original object could
	/// be made to implement that interface, but it does not and, if you don't have a way to change the original object,
	/// this is the only solution).
	/// </summary>
	public static class DuckCaster
	{
		#region Cast
			private static readonly MethodInfo _methodCast = ReflectionHelper.GetMethod(() => DuckCaster.Cast<object>(null, null)).GetGenericMethodDefinition();

			/// <summary>
			/// Tries to cast the given object to the given interface type.
			/// If the cast is not valid, tries to implement a proxy interface to redirect to it.
			/// </summary>
			public static object Cast(object source, Type toType, object securityToken=null)
			{
				if (toType == null)
					throw new ArgumentNullException("toType");

				var method = _methodCast.MakeGenericMethod(toType);
				return method.Invoke(null, new object[]{source, securityToken});
			}

			/// <summary>
			/// Tries to cast the given object to the given interface type.
			/// If the cast is not valid, tries to implement a proxy interface to redirect to it.
			/// </summary>
			public static T Cast<T>(object source, object securityToken=null)
			{
				if (source == null)
					return default(T);

				if (source is T)
				{
					T result = (T)source;
					return result;
				}

				var baseDuck = source as BaseDuck;
				if (baseDuck != null)
					return baseDuck.DuckCast<T>(securityToken);

				if (!typeof(T).IsInterface)
					throw new ArgumentException("Cast to " + typeof(T).FullName + " is not valid.");

				return _Cast<T>(source, securityToken);
			}

			internal static T _Cast<T>(object source, object securityToken)
			{
				Type type = source.GetType();

				return _FinalCast<T>(source, type, securityToken);
			}

			internal static T _FinalCast<T>(object source, Type type, object securityToken)
			{
				Type resultType = _ImplementRedirector(typeof(T), type, true);
				ConstructorInfo constructor = resultType.GetConstructors()[0];
				object untypedResult = constructor.Invoke(new object[] { source, securityToken });
				T result = (T)untypedResult;
				return result;
			}
		#endregion
		#region GetStaticInterface
			private static readonly Dictionary<KeyValuePair<Type, Type>, object> _staticInterfaces = new Dictionary<KeyValuePair<Type, Type>, object>();
			/// <summary>
			/// This method will generate an implemented object for interface type T, redirecting
			/// its methods, properties and events to the static properties, methods and events of
			/// a given type. This way, you can have "virtual static methods".
			/// </summary>
			public static T GetStaticInterface<T>(Type typeWithStaticMembers)
			{
				if (typeWithStaticMembers == null)
					throw new ArgumentNullException("typeWithStaticMembers");

				if (!typeof(T).IsInterface)
					throw new ArgumentException(typeof(T).FullName + " is not an interface type.");

				object result;
				var key = new KeyValuePair<Type, Type>(typeof(T), typeWithStaticMembers);
				result = _GetStaticInterface<T>(typeWithStaticMembers, key);
				return (T)result;
			}

			internal static object _GetStaticInterface<T>(Type typeWithStaticMembers, KeyValuePair<Type, Type> key)
			{
				object result;
				lock (_staticInterfaces)
				{
					if (!_staticInterfaces.TryGetValue(key, out result))
					{
						Type resultType = _ImplementRedirector(typeof(T), typeWithStaticMembers, false);
						result = resultType.GetConstructor(Type.EmptyTypes).Invoke(null);

						_staticInterfaces.Add(key, result);
					}
				}
				return result;
			}
		#endregion

		#region _ImplementRedirector
			private static readonly Dictionary<KeyValuePair<Type, Type>, Type> _implementedRedirectors = new Dictionary<KeyValuePair<Type, Type>, Type>();

			private static readonly MethodInfo _methodGetType = ReflectionHelper<BaseDuckForTypes>.GetProperty((obj) => obj.Target).GetGetMethod();
			private static readonly ConstructorInfo _constructorNotSupportedException = ReflectionHelper.GetConstructor(() => new NotSupportedException());

			internal static Type _ImplementRedirector(Type interfaceType, Type toType, bool hasInstance)
			{
				var pair = new KeyValuePair<Type, Type>(interfaceType, toType);

				Type result;
				lock(_implementedRedirectors)
				{
					if (!_implementedRedirectors.TryGetValue(pair, out result))
					{
						TypeAttributes typeAttributes = TypeAttributes.Public | TypeAttributes.Sealed;

						if (toType.IsSerializable)
							typeAttributes |= TypeAttributes.Serializable;

						var type = 
							_DynamicModule.DefineType
							(
								string.Concat("Pfz.DynamicTypes.Caster.From_", interfaceType.Name, "_To_", toType.Name),
								typeAttributes,
								hasInstance?typeof(BaseDuckForInstances):typeof(BaseDuckForTypes),
								new Type[]{interfaceType}
							);

						if (hasInstance)
						{
							var constructor = 
								type.DefineConstructor
								(
									MethodAttributes.Public | MethodAttributes.Final,
									CallingConventions.Standard,
									new Type[]{toType, typeof(object)}
								);

							var constructorGenerator = constructor.GetILGenerator();
							constructorGenerator.EmitLoadArgument(0);
							constructorGenerator.EmitLoadArgument(1);
							constructorGenerator.EmitLoadArgument(2);
							constructorGenerator.Emit(OpCodes.Call, _constructorBaseStructuralForInstances);
							constructorGenerator.EmitReturn();
						}
						else
						{
							type.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.Final);
						}

						if (!hasInstance)
						{
							var getType = 
								type.DefineMethod
								(
									"get_Type",
									MethodAttributes.SpecialName | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.Public,
									typeof(Type),
									Type.EmptyTypes
								);
							var getTypeGenerator = getType.GetILGenerator();
							getTypeGenerator.FullLoadToken(toType);
							getTypeGenerator.EmitReturn();
							type.DefineMethodOverride(getType, _methodGetType);
						}

						var usedNames = new HashSet<string>();
						var methodDictionary = new Dictionary<MethodInfo, MethodBuilder>();

						_ImplementRedirector(type, interfaceType, toType, hasInstance, usedNames, methodDictionary);
						foreach(var baseInterface in interfaceType.GetInterfaces())
							_ImplementRedirector(type, baseInterface, toType, hasInstance, usedNames, methodDictionary);

						result = type.CreateType();
                        _implementedRedirectors.Add(pair, result);
					}
				}
				return result;
			}

			private static readonly ConstructorInfo _constructorBaseStructuralForInstances = typeof(BaseDuckForInstances).GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic)[0];
			private static readonly FieldInfo _fieldBaseStructuralForInstances = ReflectionHelper<BaseDuckForInstances>.GetField((obj) => (obj)._target);
			private static readonly MethodInfo _methodGetField = ReflectionHelper<Type>.GetMethod((type) => type.GetField("someField"));
			private static readonly MethodInfo _methodGetValue = ReflectionHelper<FieldInfo>.GetMethod((fieldInfo) => fieldInfo.GetValue(null));

			[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
			private static void _ImplementRedirector(TypeBuilder type, Type interfaceType, Type toType, bool hasInstance, HashSet<string> usedNames, Dictionary<MethodInfo, MethodBuilder> methodDictionary)
			{
				if (interfaceType == typeof(ISerializable))
					return;

				methodDictionary.Clear();
				FieldInfo instanceField = null;

				if (hasInstance)
				{
					instanceField = _fieldBaseStructuralForInstances;

					if (interfaceType.IsAssignableFrom(toType))
					{
						_SimpleRedirect(type, interfaceType, toType, usedNames, methodDictionary);
						_ImplementPropertiesAndEvents(type, interfaceType, methodDictionary);
						return;
					}
				}

				ILGenerator staticConstructorGenerator = null;
				foreach(var oldMethod in interfaceType.GetMethods())
				{
					string name = oldMethod.Name;
					Type oldReturnType = oldMethod.ReturnType;
					var oldParameterTypes = oldMethod.GetParameterTypes();

					var newMethod = _DefineNewMethod(type, usedNames, oldMethod, oldReturnType, oldParameterTypes, methodDictionary);

					if (oldMethod.IsGenericMethodDefinition)
						_DefineGenericParameters(oldMethod, newMethod);

					type.DefineMethodOverride(newMethod, oldMethod);

					var generator = newMethod.GetILGenerator();

					MethodBase toMethod;
					MethodInfo toMethodInfo;
					ConstructorInfo toConstructorInfo;
					
					Type newReturnType = null;
					CastAttribute resultCastAttribute = oldMethod.GetCustomAttribute<CastAttribute>();
					if (oldMethod.ContainsCustomAttribute<CallConstructorAttribute>())
					{
						if (oldMethod.IsGenericMethodDefinition)
						{
							_EmitNotSupported(generator);
							continue;
						}

						toConstructorInfo = toType.GetCompatibleConstructor(true, true, oldParameterTypes);;
						toMethod = toConstructorInfo;
						toMethodInfo = null;
						newReturnType = toType;

						if (!_ValidateReturnType(oldReturnType, newReturnType, resultCastAttribute, generator))
							continue;
					}
					else
					{
						toConstructorInfo = null;

						BindingFlags bindingFlags;
						if (hasInstance)
							bindingFlags = BindingFlags.Public | BindingFlags.Instance;
						else
							bindingFlags = BindingFlags.Public | BindingFlags.Static;

						toMethodInfo = toType.GetCompatibleMethod(name, bindingFlags, true, true, oldParameterTypes);

						if (toMethodInfo != null)
						{
							if (toMethodInfo.IsGenericMethodDefinition)
							{
								toMethodInfo = _MakeGenericMethod(oldMethod, toMethodInfo);
								if (toMethodInfo == null)
								{
									_EmitNotSupported(generator);
									continue;
								}
							}

							newReturnType = toMethodInfo.ReturnType;
							if (!_ValidateReturnType(oldReturnType, newReturnType, resultCastAttribute, generator))
								continue;
						}

						toMethod = toMethodInfo;

						if (hasInstance)
						{
							generator.EmitLoadArgument(0);
							generator.EmitLoadField(instanceField);
						}

						if (toMethodInfo == null)
						{
							if (name.StartsWith("get_"))
							{
								FieldInfo field = toType.GetField(name.Substring(4));
								if (field == null)
								{
									_EmitNotSupported(generator);
									continue;
								}

								resultCastAttribute = _GetProperty(interfaceType, name).GetCustomAttribute<CastAttribute>();

								newReturnType = field.FieldType;
								if (!_ValidateReturnType(oldReturnType, newReturnType, resultCastAttribute, generator))
									continue;

								if (hasInstance)
									generator.EmitLoadField(field);
								else
								{
									if (field.IsLiteral)
									{
										if (resultCastAttribute == null && !oldReturnType.IsAssignableFrom(newReturnType))
										{
											_EmitNotSupported(generator);
											continue;
										}

										if (staticConstructorGenerator == null)
										{
											var staticConstructor = 
												type.DefineConstructor
												(
													MethodAttributes.Static | MethodAttributes.Public,
													CallingConventions.Standard,
													Type.EmptyTypes
												);

											staticConstructorGenerator = staticConstructor.GetILGenerator();
										}

										staticConstructorGenerator.FullLoadToken(toType);
										staticConstructorGenerator.EmitLoadString(field.Name);
										staticConstructorGenerator.Emit(OpCodes.Callvirt, _methodGetField);
										staticConstructorGenerator.EmitLoadNull();
										staticConstructorGenerator.Emit(OpCodes.Callvirt, _methodGetValue);

										newReturnType = typeof(object);
										_EmitBoxOrCast(staticConstructorGenerator, resultCastAttribute, newReturnType, oldReturnType);

										var staticField = 
											type.DefineField
											(
												string.Concat("<<", toType.FullName, '.', name, ">>"),
												newReturnType,
												FieldAttributes.Public | FieldAttributes.InitOnly | FieldAttributes.Static
											);

										staticConstructorGenerator.EmitStoreStaticField(staticField);
										generator.EmitLoadStaticField(staticField);
										generator.EmitReturn();
										continue;
									}
									else
										generator.EmitLoadStaticField(field);
								}
							}
							else
							if (name.StartsWith("set_"))
							{
								FieldInfo field = toType.GetField(name.Substring(4));
								if (field == null)
								{
									_EmitNotSupported(generator);
									continue;
								}

								resultCastAttribute = _GetProperty(interfaceType, name).GetCustomAttribute<CastAttribute>();

								newReturnType = typeof(void);
								generator.EmitLoadArgument(1);

								var oldParameterType = oldParameterTypes[0];
								var newParameterType = field.FieldType;

								_EmitBoxOrCast(generator, resultCastAttribute, oldParameterType, newParameterType);

								if (hasInstance)
									generator.EmitStoreField(field);
								else
									generator.EmitStoreStaticField(field);
							}
							else
							{
								_EmitNotSupported(generator);
								continue;
							}
						}
					}

					if (toMethod != null)
					{
						var oldParameters = oldMethod.GetParameters();
						var newParameters = toMethod.GetParameters();
						List<LocalBuilder> localsForCast = null;
						for(int i=0; i<oldParameterTypes.Length; i++)
						{
							var oldParameter = oldParameters[i];
							var newParameter = newParameters[i];

							var oldParameterType = oldParameterTypes[i];
							var newParameterType = newParameter.ParameterType;

							CastAttribute castAttribute = oldParameters[i].GetCustomAttribute<CastAttribute>();
							bool emitted = false;
							if (oldParameterType != newParameterType)
							{
								if (oldParameterType.IsByRef != newParameterType.IsByRef)
								{
									_EmitNotSupported(generator);
									break;
								}

								if (newParameterType.IsByRef)
								{
									if (localsForCast == null)
										localsForCast = new List<LocalBuilder>();

									var oldElement = oldParameterType.GetElementType();
									var newElement = newParameterType.GetElementType();

									var localForCast = generator.DeclareLocal(newElement);
									localsForCast.Add(localForCast);

									if (!newParameter.IsOut && !oldParameter.IsOut)
									{
										generator.EmitLoadArgument(i+1);
										generator.Emit(OpCodes.Ldobj, oldElement);

										_EmitBoxOrCast(generator, castAttribute, oldElement, newElement);

										generator.EmitStoreLocal(localForCast);
									}

									generator.Emit(OpCodes.Ldloca, localForCast);
									emitted = true;
								}
							}
							
							if (!emitted)
							{
								generator.EmitLoadArgument(i+1);

								_EmitBoxOrCast(generator, castAttribute, oldParameterType, newParameterType);
							}
						}

						if (toMethodInfo != null)
							generator.Emit(OpCodes.Call, toMethodInfo);
						else
							generator.Emit(OpCodes.Newobj, toConstructorInfo);

						if (localsForCast != null)
						{
							int localIndex = -1;
							for(int i=0; i<oldParameterTypes.Length; i++)
							{
								var oldParameter = oldParameters[i];
								var newParameter = newParameters[i];

								var oldParameterType = oldParameterTypes[i];
								var newParameterType = newParameter.ParameterType;

								if (oldParameterType != newParameterType && oldParameterType.IsByRef)
								{
									localIndex++;
									var oldElement = oldParameterType.GetElementType();

									generator.EmitLoadArgument(i+1);
									generator.EmitLoadLocal(localsForCast[localIndex]);

									CastAttribute castAttribute = oldParameters[i].GetCustomAttribute<CastAttribute>();
									_EmitBoxOrCast(generator, castAttribute, newParameterType.GetElementType(), oldElement);

									generator.Emit(OpCodes.Stobj, oldElement);
								}
							}
						}
					}

					if (oldReturnType == typeof(void) && newReturnType != typeof(void))
						generator.Emit(OpCodes.Pop);
					else
					{
						if (resultCastAttribute != null)
						{
							if (oldReturnType == typeof(void))
								throw new ArgumentException("Method " + name + " of interface type " + interfaceType.FullName + " does not returns a value, but is marked with [CastAttribute].");

							if (!oldReturnType.IsInterface)
								throw new ArgumentException("Method " + name + " of interface type " + interfaceType.FullName + " is marked with [CastAttribute], but its result is not an interface type.");
						}

						_EmitBoxOrCast(generator, resultCastAttribute, newReturnType, oldReturnType);
					}

					generator.EmitReturn();
				}

				if (staticConstructorGenerator != null)
					staticConstructorGenerator.EmitReturn();

				_ImplementPropertiesAndEvents(type, interfaceType, methodDictionary);
			}
		#endregion
		#region _DefineNewMethod
			private static MethodBuilder _DefineNewMethod(TypeBuilder type, HashSet<string> usedNames, MethodInfo oldMethod, Type oldReturnType, Type[] oldParameterTypes, Dictionary<MethodInfo, MethodBuilder> methodDictionary)
			{
				string name = oldMethod.Name;

				bool isNew = usedNames.Add(name);
				if (!isNew)
					name = string.Concat(oldMethod.DeclaringType.FullName, ".", name);

				var result = type.DefineMethod
				(
					name,
					MethodAttributes.Virtual | MethodAttributes.Public | MethodAttributes.Final,
					oldReturnType,
					oldParameterTypes
				);

				if (isNew)
					methodDictionary.Add(oldMethod, result);

				return result;
			}
		#endregion
		#region _ValidateReturnType
			private static bool _ValidateReturnType(Type oldReturnType, Type newReturnType, CastAttribute resultCastAttribute, ILGenerator generator)
			{
				if (resultCastAttribute != null)
					return true;

				if (oldReturnType == typeof(void))
					return true;

				if (newReturnType.IsGenericParameter)
					return true;

				if (oldReturnType.IsGenericParameter)
					return true;

				if (newReturnType.IsAssignableFrom(oldReturnType))
					return true;

				if (oldReturnType.IsAssignableFrom(newReturnType))
					return true;

				if (!((newReturnType.IsEnum && oldReturnType == typeof(string)) || (oldReturnType.IsEnum && newReturnType == typeof(string))))
				{
					_EmitNotSupported(generator);
					return false;
				}

				return true;
			}
		#endregion
		#region _ImplementPropertiesAndEvents
			private static void _ImplementPropertiesAndEvents(TypeBuilder type, Type interfaceType, Dictionary<MethodInfo, MethodBuilder> methodDictionary)
			{
				foreach(var oldProperty in interfaceType.GetProperties())
				{
					MethodBuilder getMethod = null;
					MethodBuilder setMethod = null;

					if (oldProperty.CanRead)
						if (!methodDictionary.TryGetValue(oldProperty.GetGetMethod(), out getMethod))
							continue;

					if (oldProperty.CanWrite)
						if (!methodDictionary.TryGetValue(oldProperty.GetSetMethod(), out setMethod))
							continue;

					string name = oldProperty.Name;
					Type returnType = oldProperty.PropertyType;
					var parameterTypes = oldProperty.GetIndexParameters().GetTypes();

					var newProperty = 
						type.DefineProperty
						(
							name,
							PropertyAttributes.None,
							returnType,
							parameterTypes
						);

					if (getMethod != null)
						newProperty.SetGetMethod(getMethod);

					if (setMethod != null)
						newProperty.SetSetMethod(setMethod);
				}

				foreach(var oldEvent in interfaceType.GetEvents())
				{
					MethodBuilder addMethod;
					MethodBuilder removeMethod;

					if (!methodDictionary.TryGetValue(oldEvent.GetAddMethod(), out addMethod))
						continue;

					if (!methodDictionary.TryGetValue(oldEvent.GetRemoveMethod(), out removeMethod))
						continue;

					string name = oldEvent.Name;
					Type eventType = oldEvent.EventHandlerType;

					var newEvent =
						type.DefineEvent
						(
							name,
							EventAttributes.None,
							eventType
						);

					newEvent.SetAddOnMethod(addMethod);
					newEvent.SetRemoveOnMethod(removeMethod);
				}
			}
		#endregion
		#region _GetProperty
			private static PropertyInfo _GetProperty(Type interfaceType, string name)
			{
				name = name.Substring(4);

				var result = interfaceType.GetProperty(name);
				if (result == null)
					throw new ArgumentException("Can't find property " + name + " in type " + interfaceType.FullName + ".");

				return result;
			}
		#endregion
		#region _EmitCast
			private static readonly MethodInfo _methodStructuralCast = ReflectionHelper.GetMethod(() => StructuralCaster.Cast<IDisposable>(null, null)).GetGenericMethodDefinition();
			private static readonly MethodInfo _methodDuckCast = ReflectionHelper.GetMethod(() => DuckCaster.Cast<IDisposable>(null, null)).GetGenericMethodDefinition();
			private static readonly FieldInfo _fieldSecurityToken = ReflectionHelper<BaseDuckForInstances>.GetField((obj) => obj._securityToken);
			private static void _EmitCast(ILGenerator generator, CastAttribute castAttribute, Type castType)
			{
				if (castAttribute.MustUseSecurityToken)
				{
					generator.EmitLoadArgument(0);
					generator.EmitLoadField(_fieldSecurityToken);
				}
				else
					generator.EmitLoadNull();

				MethodInfo method;
				if (castAttribute.MustValidateStructure)
					method = _methodStructuralCast;
				else
					method = _methodDuckCast;

				method = method.MakeGenericMethod(castType);

				generator.Emit(OpCodes.Call, method);
			}
		#endregion
		#region _EmitBoxOrCast
			private static readonly MethodInfo _methodToString = ReflectionHelper<object>.GetMethod((obj) => obj.ToString());
			private static readonly MethodInfo _methodEnumParserParse = ReflectionHelper.GetMethod(() => EnumParser.Parse<GCCollectionMode>(null)).GetGenericMethodDefinition();
			private static void _EmitBoxOrCast(ILGenerator generator, CastAttribute castAttribute, Type oldParameterType, Type newParameterType)
			{
				if (oldParameterType == newParameterType)
					return;

				if (oldParameterType.IsGenericParameter)
				{
					generator.Emit(OpCodes.Box, oldParameterType);

					if (newParameterType == typeof(object))
						return;

					oldParameterType = typeof(object);
				}

				if (oldParameterType.IsValueType == newParameterType.IsValueType)
				{
					if (!newParameterType.IsAssignableFrom(oldParameterType))
					{
						if (castAttribute != null)
							_EmitCast(generator, castAttribute, newParameterType);
						else
						if (newParameterType.IsGenericParameter)
							generator.Emit(OpCodes.Unbox_Any, newParameterType);
						else
							generator.Emit(OpCodes.Castclass, newParameterType);
					}
				}
				else
				{
					if (newParameterType.IsValueType)
					{
						if (oldParameterType == typeof(string) && newParameterType.IsEnum)
							generator.Emit(OpCodes.Call, _methodEnumParserParse.MakeGenericMethod(newParameterType));
						else
							generator.Emit(OpCodes.Unbox_Any, newParameterType);
					}
					else
					{
						if (!newParameterType.IsGenericParameter || castAttribute != null)
							generator.Emit(OpCodes.Box, oldParameterType);

						if (castAttribute != null)
							_EmitCast(generator, castAttribute, newParameterType);
						else
						if (newParameterType == typeof(string) && oldParameterType.IsEnum)
							generator.Emit(OpCodes.Callvirt, _methodToString);
					}
				}
			}
		#endregion
		#region _EmitNotSupported
			private static void _EmitNotSupported(ILGenerator generator)
			{
				generator.EmitNewObject(_constructorNotSupportedException);
				generator.Emit(OpCodes.Throw);
			}
		#endregion
		#region _SimpleRedirect
			private static void _SimpleRedirect(TypeBuilder type, Type interfaceType, Type toType, HashSet<string> usedNames, Dictionary<MethodInfo, MethodBuilder> methodDictionary)
			{
				foreach(var interfaceMethod in interfaceType.GetMethods())
				{
					string name = interfaceMethod.Name;
					Type returnType = interfaceMethod.ReturnType;
					Type[] parameterTypes = interfaceMethod.GetParameterTypes();

					MethodInfo toMethod = null;

					if (!type.IsGenericType && !interfaceMethod.IsGenericMethod)
					{
						string toMethodName = string.Concat(type.FullName, '.', name);

						toMethod = 
							toType.GetMethod
							(
								toMethodName,
								BindingFlags.NonPublic | BindingFlags.Instance,
								null,
								parameterTypes,
								null
							);
					}

					if (toMethod == null)
					{
						toMethod = 
							toType.GetMethod
							(
								name,
								parameterTypes
							);
					}

					var newMethod = _DefineNewMethod(type, usedNames, interfaceMethod, returnType, parameterTypes, methodDictionary);

					if (interfaceMethod.IsGenericMethodDefinition)
						_DefineGenericParameters(interfaceMethod, newMethod);

					var generator = newMethod.GetILGenerator();

					generator.EmitLoadArgument(0);
					generator.EmitLoadField(_fieldBaseStructuralForInstances);

					for(int i=1; i<=parameterTypes.Length; i++)
						generator.EmitLoadArgument(i);

					if (toMethod != null)
					{
						generator.Emit(OpCodes.Call, toMethod);
						_EmitBoxOrCast(generator, null, toMethod.ReturnType, interfaceMethod.ReturnType);
					}
					else
						generator.Emit(OpCodes.Callvirt, interfaceMethod);

					generator.EmitReturn();

					type.DefineMethodOverride(newMethod, interfaceMethod);
				}
			}
		#endregion
		#region _DefineGenericParameters
			private static void _DefineGenericParameters(MethodInfo interfaceMethod, MethodBuilder newMethod)
			{
				var arguments = interfaceMethod.GetGenericArguments();
				string[] names = new string[arguments.Length];

				for (int i = 0; i < arguments.Length; i++)
					names[i] = arguments[i].Name;

				newMethod.DefineGenericParameters(names);
			}
		#endregion
		#region _MakeGenericMethod
			internal static MethodInfo _MakeGenericMethod(MethodInfo fromMethodInfo, MethodInfo toMethodInfo)
			{
				Dictionary<Type, Type> dictionary = new Dictionary<Type, Type>();

				foreach(var genericArgument in toMethodInfo.GetGenericArguments())
					dictionary.Add(genericArgument, null);

				var fromParameters = fromMethodInfo.GetParameters();
				int index = -1;
				foreach(var toParameterInfo in toMethodInfo.GetParameters())
				{
					index++;
					var toParameterType = toParameterInfo.ParameterType;
					if (toParameterType.IsByRef)
						toParameterType = toParameterType.GetElementType();

					if (!toParameterType.IsGenericParameter)
						continue;

					Type fromParameterType = dictionary[toParameterType];

					if (fromParameterType == null)
					{
						fromParameterType = fromParameters[index].ParameterType;

						if (fromParameterType.IsByRef)
							fromParameterType = fromParameterType.GetElementType();

						dictionary[toParameterType] = fromParameterType;
					}
					else
					{
						if (fromParameterType != fromParameters[index].ParameterType)
							return null;
					}
				}

				if (toMethodInfo.ReturnType.IsGenericParameter)
				{
					var expectedResultType = dictionary[toMethodInfo.ReturnType];

					if (expectedResultType == null)
						dictionary[toMethodInfo.ReturnType] = fromMethodInfo.ReturnType;
					else
					if (expectedResultType != fromMethodInfo.ReturnType)
						return null;
				}

				foreach(var value in dictionary.Values)
					if (value == null)
						return null;

				return toMethodInfo.MakeGenericMethod(dictionary.Values.ToArray());
			}
		#endregion
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Microsoft
United States United States
I started to program computers when I was 11 years old, as a hobbyist, programming in AMOS Basic and Blitz Basic for Amiga.
At 12 I had my first try with assembler, but it was too difficult at the time. Then, in the same year, I learned C and, after learning C, I was finally able to learn assembler (for Motorola 680x0).
Not sure, but probably between 12 and 13, I started to learn C++. I always programmed "in an object oriented way", but using function pointers instead of virtual methods.

At 15 I started to learn Pascal at school and to use Delphi. At 16 I started my first internship (using Delphi). At 18 I started to work professionally using C++ and since then I've developed my programming skills as a professional developer in C++ and C#, generally creating libraries that help other developers do their work easier, faster and with less errors.

Want more info or simply want to contact me?
Take a look at: http://paulozemek.azurewebsites.net/
Or e-mail me at: paulozemek@outlook.com

Codeproject MVP 2012, 2015 & 2016
Microsoft MVP 2013-2014 (in October 2014 I started working at Microsoft, so I can't be a Microsoft MVP anymore).

Comments and Discussions