/// <summary> /// Create an implementation, or reuse an existing, for an interface. /// Create an instance, add intercepting code, which implements a range of interfaces /// </summary> /// <param name="interfaceType">Type</param> /// <returns>implementation</returns> public static IExtensibleInterceptor New(Type interfaceType) { // create the intercepted object if (!interfaceType.IsVisible) { throw new ArgumentException("Internal types are not allowed.", interfaceType.Name); } if (!interfaceType.IsInterface) { throw new ArgumentException("Only interfaces are allowed.", nameof(interfaceType)); } // GetInterfaces doesn't return the type itself, so we need to add it. var implementingInterfaces = new[] { interfaceType }.Concat(interfaceType.GetInterfaces()).ToList(); var implementingAndDefaultInterfaces = new List <Type>(); foreach (var implementingInterface in implementingInterfaces.ToList()) { implementingAndDefaultInterfaces.Add(implementingInterface); if (DefaultInterfacesMap.TryGetValue(implementingInterface, out var defaultInterfaces)) { implementingAndDefaultInterfaces.AddRange(defaultInterfaces); } } implementingInterfaces = implementingAndDefaultInterfaces.Distinct().ToList(); // Create an implementation, or lookup Type implementingType; lock (TypeMap) { if (!TypeMap.TryGetValue(interfaceType, out implementingType)) { int typeIndex = 0; // Build a name for the type var typeName = interfaceType.Name + "Impl{0}"; // Remove "I" at the start if (typeName.StartsWith("I")) { typeName = typeName.Substring(1); } var fqTypeName = interfaceType.FullName?.Replace(interfaceType.Name, string.Format(typeName, typeIndex)); if (fqTypeName == null) { throw new InvalidOperationException($"{interfaceType} doesn't have a name?"); } // Only create it if it was not already created via another way while (TypeBuilder.TryGetType(string.Format(fqTypeName, typeIndex), out implementingType)) { // Break loop if the types are assignable if (interfaceType.IsAssignableFrom(implementingType)) { Log.Verbose().WriteLine("Using cached, probably created elsewhere, type: {0}", fqTypeName); break; } Log.Verbose().WriteLine("Cached type {0} was not compatible with the interface, ignoring.", fqTypeName); fqTypeName = interfaceType.FullName.Replace(interfaceType.Name, string.Format(typeName, typeIndex++)); } if (implementingType == null) { // Use this baseType if nothing is specified var baseType = typeof(ExtensibleInterceptorImpl <>); foreach (var implementingInterface in implementingInterfaces) { if (!BaseTypeMap.ContainsKey(implementingInterface)) { continue; } baseType = BaseTypeMap[implementingInterface]; break; } // Make sure we have a non generic type, by filling in the "blanks" if (baseType.IsGenericType) { baseType = baseType.MakeGenericType(interfaceType); } implementingType = TypeBuilder.CreateType(fqTypeName, implementingInterfaces.ToArray(), baseType); } // Register the implementation for the interface TypeMap[interfaceType] = implementingType; } } if (!interfaceType.IsAssignableFrom(implementingType)) { throw new InvalidOperationException($"{interfaceType.AssemblyQualifiedName} and {implementingType.AssemblyQualifiedName} are not compatible!?"); } // Create an instance for the implementation if (!(Activator.CreateInstance(implementingType) is IExtensibleInterceptor interceptor)) { throw new InvalidCastException("Internal error, the created type didn't implement IExtensibleInterceptor."); } var genericImplementingInterfaces = implementingInterfaces.Where(x => x.IsGenericType).Select(x => x.GetGenericTypeDefinition()).ToList(); // Add the extensions foreach (var extensionType in ExtensionTypes) { var extensionAttributes = (ExtensionAttribute[])extensionType.GetCustomAttributes(typeof(ExtensionAttribute), false); foreach (var extensionAttribute in extensionAttributes) { var implementing = extensionAttribute.Implementing; if (implementingInterfaces.Contains(implementing) || genericImplementingInterfaces.Contains(implementing)) { interceptor.AddExtension(extensionType); } } } interceptor.Init(); return(interceptor); }