Как связать универсальные типы с наследованием, используя расширения 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;
}
}
КОНЕЧНЫЙ КАНДИДАТ РЕШЕНИЯ - пользовательский генератор привязок