Как связать универсальные типы с наследованием, используя расширения Ninject Conventions

Как я могу связать InitializerForXXX (неуниверсальная реализация) для IInitializer<XXX> (универсальный интерфейс) с использованием Ninject Conventions, чтобы запросить IInitializer<T> разрешить неуниверсальную реализацию, имя которой начинается с InitializerFor и заканчивается typeof(T).Name лайк:

initializerFactory.CreateFor<Blue>();        //resolves InitializerOfBlue
initializerFactory.CreateFor<ShadeOfBlue>(); //resolves InitializerOfShadeOfBlue

где нет неабстрактного класса, непосредственно реализующего IInitializer<T>и некоторые реализации наследуются от других реализаций:

  • InitializerForShadeOfBlue наследуется от InitializerForBlue
  • InitializerForBlue наследует от абстрактного Initializer<Blue>
  • Аннотация Initializer<T> непосредственно реализует IInitializer<T>

Я надеюсь, что смогу использовать .EndsWith(typeof(T).Name) для данного IInitializer<T> Я могу использовать соглашение, потому что в вене ShadeOfxxx буквально сотни инициализаторов. Если мне нужно отобразить их все, мне лучше найти способ разрешения с помощью отражения во время выполнения.

Учитывая следующее:

ОБНОВЛЕНИЕ: привязки с генератором пользовательских привязок (см. Мой ответ ниже для реализации)

    void Bootstrap(IBindingRoot kernel)
    {
        kernel.Bind<IInitializerFactory>()
            .To<InitializerFactory>()
            .InSingletonScope();

        kernel.Bind(scanner =>
                    scanner.FromThisAssembly().SelectAllClasses()
                        .WhichAreNotGeneric()
                        .InheritedFrom(typeof(IComplexContent))
                        .BindAllInterfaces());

        kernel.Bind(scanner =>
                    scanner.FromThisAssembly().SelectAllClasses()
                        .WhichAreNotGeneric()
                        .InheritedFrom(typeof(IInitializer<>))
                        .BindWith<FirstTypeParameterNameMatchesEndOfBoundClassNameGenerator>());
    }

основной метод

void Main(IEnumerable<string> values)
{
    // setup bindings
    var kernel = new StandardKernel();
    Bootstrap(kernel);

    IInitializerFactory initializerFactory = 
        kernel.Get<IInitializerFactory>();

    IInitializer<ShadeOfBlueComplexContent> initializer = 
        initializerFactory.CreateFor<ShadeOfBlueComplexContent>();

    initializer.Initialize(values);
}

фабрика инициализаторов

interface IInitializerFactory
{
    IInitializer<T> CreateFor<T>() where T : class, IComplexContent, new();
}

class InitializerFactory : IInitializerFactory
{
    public IInitializer<T> CreateFor<T>() where T : class, IComplexContent, new()
    {
        return MagicallyGetInitializer<T>();
    }

    //behind the curtain, whirring noises are heard as 't' is resolved...
    private static IInitializer<T> MagicallyGetInitializer<T>() 
        where T : class, IComplexContent, new()
    {
        IInitializer<T> i = null;
        return i;
    }
}

инициализаторы

interface IInitializer<out T> where T : IComplexContent
{
    T Initialize(IEnumerable<string> values);
}

abstract class Initializer<T> : IInitializer<T> where T : IComplexContent
{
    public abstract T Initialize(IEnumerable<string> values);
}

class InitializerOfBlue : Initializer<Blue>
{
    private readonly Blue _content;

    public InitializerOfBlue(Blue content) {_content = content;}

    public override Blue Initialize(IEnumerable<string> values)
    {
        _content.BlueSpecificProperty = values.ElementAt(0);
        //... populate other blue-specific properties like this
        return _content;
    }
}

class InitializerOfShadeOfBlue : InitializerOfBlue
{
    public InitializerOfShadeOfBlue(ShadeOfBlue content) : base(content){}
}

модели контента

interface IComplexContent
{
    string OneBasicProperty { get; set; }
    // other properties are specific to implementation
    string UniqueOperation();
}

abstract class BaseComplexContent : IComplexContent
{
    public string OneBasicProperty { get; set; }
    public abstract string UniqueOperation();
}

class Blue : BaseComplexContent
{
    // initializer sets this
    public string PropertyForAllKindsOfBlue { get; set; }

    // initializer doesn't interact with this
    public override string UniqueOperation() {return "I'm plain.";}
}

class ShadeOfBlue : Blue
{
    // initializer doesn't interact with this
    public override string UniqueOperation() {return "I'm fabulous!";}
}

2 ответа

Вы перестали указывать выбор класса

    kernel.Bind(scanner =>
                scanner.FromThisAssembly().SelectAllClasses()
                    .WhichAreNotGeneric()
                    .InheritedFrom(typeof (IInitializer<>))

Этого уже достаточно. Что вам нужно сделать, это добавить пользовательский Binding Generator. Что выбирает IInitializer<Blue> за InitializerForBlue а также IInitializer<ShadeOfBlue> за InitializerForShadeOfBlue

https://github.com/ninject/ninject.extensions.conventions/wiki/Projecting-Services-to-Bind

BEGIN SOLUTION CANDIDATE - генератор пользовательских привязок:

генератор пользовательских привязок

Спасибо за совет, @RemoGloor и @RubenBartelink. Хотя я в тупике - проблема в том, что я завязываю IInitializer<Blue> в InitializerOfShadeOfBlue, Мне нужно иметь возможность как-то изменить аргумент универсального типа из Blue в ShadeOfBlue в IInitializer<Blue> обязательный кандидат, так как IInitializer<ShadeOfBlue> это то, что будет запрошено у фабричного метода во время выполнения.

Есть ли способ изменить список аргументов универсального типа кандидата на связывание? Или я лаю не на ту реализацию? Любые предложения по редактированию моего OP или этот ответ приветствуются.

/// <summary>Creates bindings on open generic types where bound implementations'
/// names end  with the name of the generic type argument</summary>
public class FirstTypeParameterNameMatchesEndOfBoundClassNameGenerator : IBindingGenerator
{
    public IEnumerable<IBindingWhenInNamedWithOrOnSyntax<object>> CreateBindings(Type type, IBindingRoot bindingRoot)
    {
        if (type == null) throw new ArgumentNullException("type");
        if (bindingRoot == null) throw new ArgumentNullException("bindingRoot");

        // only consider concrete, non-abstract classes
        if (type.IsInterface || type.IsAbstract) yield break;

        var bindingType = GetBindingType(type);

        if (bindingType != null)
            yield return bindingRoot.Bind(bindingType).To(type);
        // ARGH! bindingType == IInitializer`1[[Blue]] but I want
        // IInitializer`1[[ShadeOfBlue]] for type == ShadeOfBlue

    }

    private static Type GetBindingType(Type type)
    {
        Type goodMatch = null;

        foreach (var candidate in type.GetInterfaces())
        {
            // skip non-generic interfaces
            if (!candidate.IsGenericType) continue;

            // assumption: using argument in first position
            var firstArg = candidate.GetGenericArguments().First();
            if (!type.Name.EndsWith(firstArg.Name)) continue;

            // IInitializer<XXX> matches InitializerOfXXX
            goodMatch = candidate;
            break;
        }
        if (goodMatch == null)
        {
            // if no match on interfaces, walk through the ancestor types
            foreach (var candidate in type.GetAllAncestors())
            {
                goodMatch = GetBindingType(candidate);
                if (goodMatch != null) break;
            }
        }
        return goodMatch;
    }

Тип Расширение помощник

public static class TypeExtensions
{
    // returns all ancestor types starting with the parent
    public static IEnumerable<Type> GetAllAncestors(this Type type)
    {
        for (var current = type.BaseType; current != null; current = current.BaseType)
            yield return current;
    }
}

КОНЕЧНЫЙ КАНДИДАТ РЕШЕНИЯ - пользовательский генератор привязок

Другие вопросы по тегам