Using NInject to bind a generic interface, with a default if a binding for the generic type is not set

后端 未结 3 1728
一向
一向 2021-02-02 02:04

Imagine I have the following classes and interfaces:

public interface IService { }

public class DefaultService : IService { }

public         


        
3条回答
  •  猫巷女王i
    2021-02-02 02:46

    I took the liberty of refactoring the answer from @cbp, so that it works for the new IBindingGenerator signature in Ninject v3 conventions. It's pretty much replacing the Process() method signature with the CreateBindings() method signature, but I didn't test this, so there's a chance you'll have to tweak it a bit if you use it.

    /// 
    /// Creates bindings on open generic types.
    /// This is similar to the out-of-the-box 
    /// , 
    /// but allows a default class to be
    /// specified if no other bindings can be found. 
    /// See the test case for usages.
    /// 
    public class GenericBindingGeneratorWithDefault : IBindingGenerator
    {
        private static readonly Type TypeOfObject = typeof(object);
        private readonly Type _contractType;
        private readonly Dictionary _cachedBindings;
        private readonly Type _defaultType;
    
        public GenericBindingGeneratorWithDefault(Type contractType, Type defaultType)
        {
            if (!(contractType.IsGenericType || contractType.ContainsGenericParameters))
                throw new ArgumentException("The contract must be an open generic type.", 
                    "contractType");
    
            _cachedBindings = new Dictionary();
            _contractType = contractType;
            _defaultType = defaultType;
        }
    
        /// 
        /// Creates the bindings for a type.
        /// 
        /// The type for which the bindings are created.
        /// The binding root that is used to create the bindings.
        /// 
        /// The syntaxes for the created bindings to configure more options.
        /// 
        public IEnumerable> CreateBindings(Type type, IBindingRoot bindingRoot)
        {
            if (type == null) throw new ArgumentNullException("type");
            if (bindingRoot == null) throw new ArgumentNullException("bindingRoot");
    
            if (type.IsInterface || type.IsAbstract) yield break;
    
            if (type == _defaultType)
            {
               yield return bindingRoot.Bind(_contractType).ToMethod(
                   ctx =>
                       {
                           Type requestedType = ctx.Request.Service;
                           Type resolution = _cachedBindings.ContainsKey(requestedType)
                                                 ? _cachedBindings[requestedType]
                                                 : _defaultType.MakeGenericType(ctx.GenericArguments);
                           return ctx.Kernel.Get(resolution);
                       });
            }
            else
            {
                Type interfaceType = ResolveClosingInterface(type);
                if (interfaceType != null)
                {
                   yield return bindingRoot.Bind(type).To(_cachedBindings[interfaceType]);
                }
            }
        }
    
        /// 
        /// Resolves the closing interface.
        /// 
        /// Type of the target.
        /// 
        private Type ResolveClosingInterface(Type targetType)
        {
            if (targetType.IsInterface || targetType.IsAbstract) return null;
    
            do
            {
                Type[] interfaces = targetType.GetInterfaces();
                foreach (Type @interface in interfaces)
                {
                    if (!@interface.IsGenericType) continue;
    
                    if (@interface.GetGenericTypeDefinition() == _contractType)
                    {
                        return @interface;
                    }
                }
                targetType = targetType.BaseType;
            } while (targetType != TypeOfObject);
    
            return null;
        }
    }
    

提交回复
热议问题