Неявное внедрение зависимости в базовый класс, в то время как производный класс разрешается через Unity

У меня есть базовый класс Base с зависимостью Dep и default и Injection Constructor-

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;            
    }
}

Я думал, что Dependency dep должен вводиться автоматически (через Constructor Injection) при разрешении производного класса.

Но это, похоже, не работает, когда я извлекаю из него класс и разрешаю этот класс, вместо этого вызывается конструктор по умолчанию для базы.

Я могу заставить это работать только тогда, когда я явно вызываю конструктор из производного класса.

class Derived : Base
{

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

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

Предоставляет ли единство какой-либо прямой способ неявного вызова конструктора внедрения базового класса (а не явным вызовом конструктора)? Если нет, то есть ли причина, по которой контейнер единства не работает сам по себе?


person Rajat Srivastava    schedule 11.09.2014    source источник
comment
Ваши проблемы вызваны наличием нескольких конструкторов. У ваших сервисов должен быть только один конструктор. Наличие нескольких конструкторов является анти-шаблоном и следует избегать.   -  person Steven    schedule 13.09.2014
comment
@Steven Я понял, что несколько конструкторов - это антипаттерн. Но удаление конструктора по умолчанию не решает цели здесь.   -  person Rajat Srivastava    schedule 14.09.2014
comment
Я думаю, что это действительно решает ваши проблемы, потому что, если и Base, и Derived содержат каждый только один конструктор, который принимает необходимые зависимости, проблема вообще не будет существовать. Однако я согласен с @BatteryBackupUnit, что наличие базового класса может быть не лучшим дизайном.   -  person Steven    schedule 15.09.2014
comment
@Steven Должны ли мы всегда включать в класс конструктор по умолчанию. У меня не может быть параметризованного конструктора без определения конструктора по умолчанию.   -  person Rajat Srivastava    schedule 16.09.2014
comment
@Steven. Даже если я пропущу конструктор по умолчанию в базовом классе, конструктор производного класса выдаст ошибку компилятора.   -  person Rajat Srivastava    schedule 16.09.2014


Ответы (3)


Нет, единство этого сделать не может. На самом деле, нет ни одного контейнера, который мог бы это сделать. Конструктор предназначен для создания экземпляра класса. Если вы вызовете два конструктора, у вас будет два экземпляра. Если базовый класс является абстрактным, вы даже не можете вызвать его конструктор (кроме производных конструкторов, как вы знаете).

Таким образом, из-за ограничений C # .net, если вы хотите использовать внедрение конструктора, он будет работать только в том случае, если вы явно вставите значение в конструктор Derived, который вызывает конструктор Base, отличный от используемого по умолчанию.

Однако вместо этого вы можете использовать внедрение свойств или методов. С их помощью вам не нужно добавлять зависимость к каждому конструктору производного класса.

Внедрение свойств:

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

Внедрение метода:

class Base
{
     private IDep dep;

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

Пожалуйста, обрати внимание:

  • Объект создается (внедрение ctor) до того, как будет выполнено внедрение метода / свойства.
  • Возможно, будет достаточно адаптировать дизайн так, чтобы базовый класс не требовался, см. Композиция вместо наследования
person BatteryBackupUnit    schedule 12.09.2014

Вот как вы должны решить проблему:

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, я не вижу причин для дальнейшего использования базовых классов. Использование композиции вместо наследования часто снижает сложность системы.

person Steven    schedule 16.09.2014
comment
Да, я могу это сделать. Но я не хотел использовать явный вызов конструктора (: base (dep)). И да, для нескольких конструкторов я согласен с вами, что мне следует избегать использования нескольких конструкторов. Я согласен с BatteryBackupUnit в том, что единство не может неявно вызывать конструктор инъекции, поскольку это ограничения C # .net - person Rajat Srivastava; 17.09.2014
comment
Я бы объявил dep как protected readonly в базовом классе. Модификатор protected разрешает производным классам доступ к dep, а readonly предотвращает его изменения. Это делает ненужным дублирование переменной dep и ее инициализацию в производном классе. - person Olivier Jacot-Descombes; 04.08.2017

Простое и прямое решение - иметь «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;
    }
}
person Dominic Savio M    schedule 16.09.2016