【问题标题】:Implicitly injecting dependency in Base class while derived class is resolved through Unity在基类中隐式注入依赖项,而派生类通过 Unity 解决
【发布时间】:2014-11-04 22:44:32
【问题描述】:

我有一个基类 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;            
    }
}

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

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

只有当我从派生类中显式调用构造函数时,我才能让它工作。

class Derived : Base
{

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

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

unity 是否提供任何直接方式来隐式调用基类的注入构造函数(而不是通过显式构造函数调用)? 如果没有,是否有任何理由说明为什么统一容器不能自己做?

【问题讨论】:

  • 您的问题是由多个构造函数引起的。您的服务应该只有一个构造函数。拥有multiple constructors is an anti-pattern,应该避免。
  • @Steven 我明白你的意思,多个构造函数是一种反模式。但是删除 Default 构造函数并不能解决这里的目的。
  • 我认为它实际上解决了你的问题,因为如果BaseDerived 都只包含一个接受所需依赖项的构造函数,那么问题根本不会存在。不过,我确实同意@BatteryBackupUnit 的观点,即拥有基类可能不是最好的设计。
  • @Steven Should we always include a default constructor in the class 。如果不定义默认构造函数,我就不能拥有参数化构造函数。
  • @Steven 即使我跳过基类中的默认构造函数,我的派生类构造函数也会给编译器错误。

标签: c# dependency-injection unity-container constructor-injection


【解决方案1】:

不,unity 无法做到这一点。实际上,没有一个容器可以做到这一点。 构造函数用于实例化一个类。如果你调用两个构造函数,你最终会得到两个实例。如果基类是抽象的,你甚至不能调用它的构造函数(你知道的派生构造函数除外)。

因此,受 C#.net 的限制,如果你想使用构造函数注入,只有将值显式注入到调用非默认 Base 构造函数的 Derived 构造函数中,它才会起作用。

但是,您可以选择使用属性或方法注入。有了这些,您不必将依赖项添加到派生类的每个构造函数中。

Property Injection

class Base
{
    [Dependency]
    public IDep Dep { get; set; }
} 

Method Injection

class Base
{
     private IDep dep;

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

请注意:

  • 在执行方法/属性注入之前实例化对象(ctor 注入)
  • 将设计调整为不需要基类可能就足够了,请参阅Composition over Inheritance

【讨论】:

    【解决方案2】:

    你应该这样解决问题:

    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 的观点,统一不能隐式调用注入构造函数,因为它是 C#.net 的限制
    • 我会在基类中将dep 声明为protected readonlyprotected 修饰符允许派生类访问dep,而readonly 阻止对其进行更改。这使得复制dep 变量及其在派生类中的初始化变得多余。
    【解决方案3】:

    简单直接的解决方案是在您的基类中添加一个“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;
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-03-08
      • 1970-01-01
      • 2020-01-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-29
      • 2012-07-30
      相关资源
      最近更新 更多