我的代码中有类似的情况,我有一个类来自两个祖先的抽象类,如下所示:
BaseAbstractClassExample <|-- AbstractClassExample <|-- ConcreteClassExample
我这样做是为了扩展框架中定义的抽象类。虽然我知道还有其他设计模式可能更适合我的情况,但我很好奇为什么这种基于约定的绑定不起作用。
using Ninject.Extensions.Conventions;
public abstract class BaseAbstractClassExample
{
public abstract int Number { get; set; }
}
public abstract class AbstractClassExample : BaseAbstractClassExample
{
public abstract bool Flag { get; set; }
}
public class ConcreteClassExample : AbstractClassExample
{
public override int Number { get; set; }
public override bool Flag { get; set; }
}
[TestMethod]
public void Concrete_classes_are_bound_to_grandfathers()
{
kernel.Bind(x => x.FromThisAssembly()
.SelectAllClasses().InheritedFrom<BaseAbstractClassExample>()
.BindBase());
AssertCanResolveBindingToType<ConcreteClassExample, ConcreteClassExample>(); // pass
AssertCanResolveBindingToType<AbstractClassExample, ConcreteClassExample>(); // pass
AssertCanResolveBindingToType<BaseAbstractClassExample, ConcreteClassExample>(); // fail
}
这是我写的测试绑定的断言方法,与我的问题相关。
private static void AssertCanResolveBindingToType<TRequestedType, TExpectedType>(params IParameter[] constructorParameters)
{
if (!typeof(TRequestedType).IsAssignableFrom(typeof(TExpectedType)))
Assert.Fail("{0} is not assignable from {1}, this binding wouldn't work anyway", typeof(TRequestedType), typeof(TExpectedType));
IEnumerable<TRequestedType> result = kernel.GetAll<TRequestedType>(constructorParameters);
var requestedTypes = result as TRequestedType[] ?? result.ToArray();
Assert.IsTrue(requestedTypes.Any(), "There are no bindings for {0} at all", typeof (TRequestedType));
Assert.IsTrue(requestedTypes.OfType<TExpectedType>().Any(),
"There are no bindings for {0} of the expected type {1}, bound types are: {2}",
typeof (TRequestedType), typeof (TExpectedType),
string.Join(", ", requestedTypes.Select(x => x.GetType().ToString()).Distinct()));
}
当我尝试上面的单元测试时,它会使用我的自定义消息断言“根本没有BaseAbstractClassExample的绑定”,这表明绑定到AbstractClassExample
正在按预期工作,但不是BaseAbstractClassExample
的绑定{1}}。
编辑:我编写了一个提供此功能的方法BindAllBaseClasses()
。我提交了拉取请求并且已获批准,因此此功能现在可在Ninject extensions conventions库中使用。
答案 0 :(得分:3)
Daniel解释说SelectAllClasses()
选择所有不抽象的类。选择抽象类是没有意义的,因为无法创建它们的实例。
因此,在您的情况下,它会选择ConcreteClassExample
。
然后BindBase()
告诉我们应该添加基类与所选类的绑定。在你的情况下它是:
Bind<AbstractClassExample>().To<ConcreteClassExample>();
现在你可以解析AbstractClassExample
,因为它有一个绑定和ConcreteClassExample
,因为它是自我绑定,Ninject会创建一个隐式自绑定,即使没有配置它。
您无法解析BaseAbstractClassExample
因为它既没有绑定也不能自绑定,因为它是抽象的。
答案 1 :(得分:1)
这是设计的。当您使用SelectAllClasses时,它不会选择Abstract类,因为使用的过滤器如下:
public IJoinFilterWhereExcludeIncludeBindSyntax SelectAllClasses()
{
return this.SelectTypes(t => t.IsClass && !t.IsAbstract);
}
尝试使用
public IJoinFilterWhereExcludeIncludeBindSyntax SelectAllIncludingAbstractClasses()
{
return this.SelectTypes(t => t.IsClass);
}
虽然这并没有向我解释为什么你能够解析AbstractClassExample。这可能是一个错误。您是否介意在会议延期上提出问题?另一个原因可能是BaseBindingGenerator不包含抽象类。