【问题标题】:BindBase only binds immediate ancestorsBindBase 只绑定直接祖先
【发布时间】:2023-12-19 22:40:01
【问题描述】:

我的代码中也有类似的情况,我有一个继承自两个祖先抽象类的类,如下所示:

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 .

编辑:我写了一个方法BindAllBaseClasses() 来提供这个功能。我提交了一个拉取请求并且它被批准了,所以这个功能现在可以在 Ninject extensions conventions 库中使用。

【问题讨论】:

    标签: c# ninject ninject-extensions


    【解决方案1】:

    正如 Daniel 解释的那样,SelectAllClasses() 选择所有非抽象类。选择抽象类没有意义,因为无法创建它们的实例。

    所以在你的情况下它选择ConcreteClassExample

    然后BindBase() 告诉应添加基类到所选类的绑定。你的情况是:

    Bind<AbstractClassExample>().To<ConcreteClassExample>();
    

    现在你可以解析AbstractClassExample,因为有一个绑定和ConcreteClassExample,因为它是自绑定的,即使没有配置,Ninject 也会创建一个隐式的自绑定。

    您无法解析BaseAbstractClassExample,因为它既没有绑定也没有自绑定,因为它是抽象的。

    【讨论】:

    • 谢谢。我认为我所追求的是与BindAllInterfaces() 类似的操作,但对于抽象类。似乎BindBase() 的行为更像BindDefaultInterface(),因为它不会遍历继承树。
    【解决方案2】:

    这是设计使然。当您使用 SelectAllClasses 时,它不会选择抽象类,因为使用的过滤器如下:

      public IJoinFilterWhereExcludeIncludeBindSyntax SelectAllClasses()
            {
                return this.SelectTypes(t => t.IsClass && !t.IsAbstract);
            }
    

    尝试使用

     public IJoinFilterWhereExcludeIncludeBindSyntax SelectAllIncludingAbstractClasses()
            {
                return this.SelectTypes(t => t.IsClass);
            }
    

    虽然这并没有向我解释为什么您能够解析 AbstractClassExample。这可能是一个错误。你介意提出关于公约延期的问题吗?另一个原因可能是 BaseBindingGenerator 不包含抽象类。

    【讨论】:

    • 一点也不,很高兴提出问题。老实说,我不确定它是一个。对我来说,扩展的文档并不完全简单。