在通过Unity解析派生类时,在基类中隐式注入依赖项

13

我有一个基类Base,它依赖于Dep并具有默认和注入构造函数 -

Class Base : IBase
 {

    public IDep Dep { get; set; }

    public Base()
    {
        Console.WriteLine("Default Constructor Base ");
    }

    [InjectionConstructor]
    public Base(IDep dep)
    {
        Console.WriteLine("Injection Constructor Base ");
        Dep = dep;            
    }
}

当我解析派生类时,我认为依赖项dep应该自动注入(通过构造函数注入)。

但是当我从它派生一个类并解析该类时,似乎这不起作用,而是调用了基类的默认构造函数。

只有在我从派生类显式地调用构造函数时,才能使它正常工作。

class Derived : Base
{

    public Derived ()
    {
        Console.WriteLine("Default Constructor Derived ");
    }

    public Derived (IDep dep) : base(dep1)
    {
        Console.WriteLine("Injection Constructor Derived ");            
    }
}

Unity是否提供任何直接的方式来隐式调用基类的注入构造函数(而不是通过显式构造函数调用)? 如果没有,那么为什么Unity容器自己不这样做?


你的问题是由于有多个构造函数引起的。你的服务应该只有一个单一的构造函数。拥有多个构造函数是一种反模式,应该避免使用。 - Steven
@Steven 我明白你的意思,多个构造函数确实是一种反模式。但是删除默认构造函数并不能解决这里的目的。 - Rajat Srivastava
我认为这实际上解决了你的问题,因为如果BaseDerived都只包含一个接受所需依赖项的构造函数,那么问题根本不存在。然而,我同意@BatteryBackupUnit的观点,拥有一个基类可能不是最好的设计。 - Steven
@Steven 我们在类中是否应该始终包含默认构造函数。如果没有定义默认构造函数,我将无法拥有参数化构造函数。 - Rajat Srivastava
即使我在基类中跳过默认构造函数,我的派生类构造函数仍会导致编译器错误。 - Rajat Srivastava
3个回答

19
不,Unity 无法做到。实际上,没有一个容器可以做到这一点。构造函数用于实例化一个类。如果调用两个构造函数,你将得到两个实例。如果基类是抽象的,你甚至不能调用它的构造函数(除了您已知的派生构造函数)。因此,由于 C#.net 的限制,如果想使用构造函数注入,只有在显式地将值注入调用非默认的 Base 构造函数的 Derived 构造函数中才能工作。然而,你可以选择使用属性或方法注入。使用这些,你不必将依赖项添加到派生类的每个构造函数中。属性注入
class Base
{
    [Dependency]
    public IDep Dep { get; set; }
} 

方法注入:

class Base
{
     private IDep dep;

     [InjectionMethod]
     public void Initialize(IDep dep)
     {
         this.dep = dep;
     }
}

请注意:

  • 对象在执行方法/属性注入之前(通过构造函数注入)被实例化
  • 将设计适应为不需要基类可能是足够的,请参阅组合优于继承

7
这是解决问题的正确方法:
class abstract Base : IBase
{
    private readonly IDep dep;

    protected Base(IDep dep)
    {
        if (dep == null) throw new ArgumentNullException("dep");
        this.dep = dep;
    }
}

现在你的基类只有一个构造函数,并且该构造函数定义了类所需的所有依赖项。这个类只有一种创建方式,而且它会保护它的不变性。依赖项被放置在一个私有字段中,因为其他类无需访问此依赖项。

有了这个基类,派生类将如下所示:

class Derived : Base
{
    private readonly IDep dep;

    public Derived(IDep dep) : base(dep)
    {
        this.dep = dep;
    }
}

这里派生类也有一个单一的构造函数来定义该类所需的依赖项。它不能以其他方式创建。如果该类使用该依赖项本身,则应将其存储在私有字段中以供以后使用。还要注意,由于这个类只有一个构造函数,所以没有任何歧义可以调用哪个构造函数,也没有理由标记构造函数使用 [InjectionConstructor] 属性。
请注意,我同意 BatteryBackupUnit 的观点。由于我将依赖注入和 SOLID 原则应用于我的应用程序中,因此我认为不再需要使用基类。使用组合而不是继承通常会降低系统的复杂性。

是的,我可以这样做。但我的重点不是使用显式构造函数调用(:base(dep))。对于多个构造函数,我同意您的观点,应该避免使用多个构造函数。 我同意BatteryBackupUnit的看法,Unity不能隐式调用注入构造函数,因为这是C#.net的限制。 - Rajat Srivastava
1
我会在基类中将dep声明为protected readonlyprotected修饰符允许派生类访问dep,而readonly则防止对其进行更改。这使得在派生类中复制dep变量及其初始化变得多余。 - Olivier Jacot-Descombes

0
简单而直接的解决方案是在您的基类中拥有一个“InjectionMethod”。
> public abstract class Base : IBase
>     {
> 
>         private IDep dep;
> 
>         [InjectionMethod]
>         public void Initialize(IDep dep)
>         {
>             if (dep == null) throw new ArgumentNullException("dep");
>             this.dep = dep;
> 
>             OnInitialize();
>         }
> 
>         public dep DepProperty
>         {
>             get
>             {
>                 return dep;
>             }
>         }
>         protected abstract void OnInitialize();
>     }

//现在你的派生类构造函数不再被强制要求具有IDep参数

class Derived : Base
{
    public Derived()
    {

    }

    protected override void OnInitialize()
    {
        // you can access the baseclass dependency Instance in this override
        object depObject = this.DepProperty;
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接