4

Ninject ConventionsInitializerForXXXを使用して(非ジェネリック実装)をIInitializer<XXX>(ジェネリックインターフェイス) にバインドして、名前がInitializerForで始まり、次のように終わる非ジェネリック実装を解決するようにするにはどうすればよいですか?IInitializer<T>typeof(T).Name

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

非抽象クラスが直接実装されておらずIInitializer<T>、一部の実装が他の実装から継承している場合:

  • InitializerForShadeOfBlueから継承InitializerForBlue
  • InitializerForBlue抽象から継承Initializer<Blue>
  • 抽象Initializer<T>は直接実装しますIInitializer<T>

ShadeOfxxxの静脈には文字通り何百もの初期化子があるので、使用できる.EndsWith(typeof(T).Name)特定IInitializer<T>の規則にを使用できることを望んでいます。それらすべてをマップする必要がある場合は、実行時にリフレクションを使用して解決する方法を見つけたほうがよいでしょう。

次の場合:

更新:カスタムバインディングジェネレーターを使用したバインディング(実装については、以下の私の回答を参照してください)

    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!";}
}
4

2 に答える 2

6

クラスの選択を指定しすぎています

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

これで十分です。ただし、カスタム バインディング ジェネレーターを追加する必要があります。IInitializer<Blue>forInitializerForBlueIInitializer<ShadeOfBlue>forを選択するInitializerForShadeOfBlue

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

于 2013-03-06T23:51:52.643 に答える
0

BEGIN SOLUTION CANDIDATE - カスタム バインディング ジェネレーター:

カスタムバインディングジェネレーター

アドバイスをありがとう、@RemoGloor と @RubenBartelink。私は困惑しています - 問題は、私が to をバインドしIInitializer<Blue>てしまうことInitializerOfShadeOfBlueです。実行時にファクトリ メソッドから要求されるものであるため、バインド候補でジェネリック型引数を何らかの方法で からBlueに変更できる必要があります。ShadeOfBlueIInitializer<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;
    }
}

END SOLUTION CANDIDATE - カスタム バインディング ジェネレーター

于 2013-03-08T02:14:19.517 に答える