Может ли кто-нибудь объяснить магию, происходящую в методе «Prism» «resol»?

У меня есть CustomersModule.cs со следующим методом Initialize ():

public void Initialize()
{
    container.RegisterType<ICustomersRepository, CustomersRepository>(new ContainerControlledLifetimeManager());
    CustomersPresenter customersPresenter = this.container.Resolve<CustomersPresenter>();

}

класс, который я разрешаю из контейнера, выглядит так:

class CustomersPresenter
{
    private CustomersView view;
    private ICustomersRepository customersRespository;

    public CustomersPresenter(CustomersView view, 
        ICustomersRepository customersRepository, 
        TestWhatever testWhatever)
    {
        this.view = view;
        this.customersRespository = customersRepository;
    }
}

Класс TestWhatever - это просто созданный мной фиктивный класс:

public class TestWhatever
{
    public string Title { get; set; }

    public TestWhatever()
    {
        Title = "this is the title";
    }

}

Тем не менее, контейнер успешно разрешает CustomersPresenter, хотя я его никогда не регистрировал, а также контейнер каким-то образом находит TestWhatever, создает его и внедряет в CustomersPresenter .

Я был очень удивлен, узнав об этом, так как я не нашел нигде в документации Prism, в которой прямо говорилось бы, что контейнер был таким автоматическим.

Так что это здорово, но что еще делает контейнер, о чем я не знаю, то есть что еще он может делать, о чем я не знаю? Например, могу ли я внедрить классы из других модулей, и если модули будут загружены, контейнер будет внедрять их, а если нет, он будет вводить ноль?


person Edward Tanguay    schedule 20.07.2009    source источник


Ответы (2)


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

class Fred { };

Fred f1 = new Fred();

Type t = typeof(Fred);

Fred f2 = (Fred)t.GetConstructor(Type.EmptyTypes).Invoke(null);

Последняя строка выше - это то, что происходит, тип t был найден с помощью typeof в параметре типа, который вы передаете Resolve.

Если тип не может быть создан с помощью new (потому что он находится в какой-то неизвестной отдельной кодовой базе), вы не сможете передать его в качестве параметра типа для Resolve.

Во втором случае это внедрение конструктора, но это все еще известный конкретный конструктивный тип. С помощью отражения платформа Unity может получить в конструктор массив всех типов параметров. Тип TestWhatever является конструируемым, поэтому нет двусмысленности или затруднений относительно того, что строить.

Что касается вашего беспокойства по поводу отдельных модулей (сборок), если вы переместите TestWhatever в другую сборку, это не изменит написанных вами строк кода; это просто будет означать, что вам нужно добавить ссылку на другую сборку, чтобы она была построена. Кроме того, TestWhatever по-прежнему является конструктивным типом с однозначной ссылкой, поэтому он может быть сконструирован с помощью Unity.

Другими словами, если вы можете ссылаться на тип в коде, вы можете получить объект Type, и поэтому во время выполнения он будет напрямую сконструирован.

Ответ на комментарий:

Если вы удалите класс TestWhatever, вы получите ошибку времени компиляции, потому что вы ссылаетесь на этот тип в своем коде. Таким образом, получить время выполнения будет невозможно.

Разделение по-прежнему действует в этой схеме, потому что вы можете зарегистрировать конкретный экземпляр TestWhatever, поэтому каждый вызов Resolve<TestWhatever>() будет получать один и тот же экземпляр, а не создавать новый.

person Daniel Earwicker    schedule 20.07.2009
comment
Но тогда где же здесь развязка, то есть если мне нужен класс MenuManager из другого модуля, и этот модуль не загружается, я понимаю, что контейнер должен, например, ввести нуль, чтобы приложение могло работать с другими частями или без них, но если я, например, Удалите класс Test Какое бы приложение ни получило ошибку. - person Edward Tanguay; 20.07.2009
comment
Подумайте: как вы собираетесь указать, что MenuManager должен быть создан? - person Daniel Earwicker; 20.07.2009
comment
Прежде чем среда CLR выполнит выражение типа Resolve<MenuManager>();, она должна загрузить сборку, содержащую определение MenuManager. Если вы ссылаетесь на тип в своем коде, вы вызываете загрузку сборки, содержащей это определение типа. - person Daniel Earwicker; 20.07.2009
comment
Было бы неплохо, если бы при создании класса в любом модуле я мог определить все, что мне нужно, в моем конструкторе, например. ... (MenuManager menuManager, ...), и если бы он существовал, я бы его получил, если бы я не получил null, у меня был бы if (menuManager! = Null), и все осталось бы развязанным, но когда я delete TestWhatever, приложение выдает ошибку, которая, кажется, является примером связывания вместо разделения, или что мне не хватает? - person Edward Tanguay; 20.07.2009
comment
Но вы указываете конкретный конструктивный тип. Это - по определению - не развязано. Чтобы получить развязку, укажите interface, которому должен удовлетворять объект, вместо конкретного конструируемого класса. - person Daniel Earwicker; 20.07.2009
comment
хорошо, но даже в случае интерфейса это казалось связанным, например когда я комментирую container.RegisterType ‹ICustomersRepository, CustomersRepository› (новый ContainerControlledLifetimeManager ()); Я получаю сообщение о невозможности разрешения зависимости от конструктора, у которого есть параметр ICustomersRepository customersRepository. Если мне нужно убедиться, что все объекты, которые я запрашиваю в своем конструкторе, действительно существуют, тогда архитектура Prism менее разделена, чем я думал. Итак, я предполагаю, что разделение происходит из-за того, что я могу запросить параметр IMenuManager menuManager и знать, что - person Edward Tanguay; 20.07.2009
comment
Самый простой способ иметь дополнительный компонент - использовать IFoo (интерфейс) для указания того, что вам нужно, а затем настроить систему на использование некоторого NullFoo : IFoo, которое будет фиктивной реализацией, которая ничего не делает (или настолько близка к нулю, насколько это имеет смысл для IFoo). - person Daniel Earwicker; 20.07.2009
comment
... (продолжение) по крайней мере, я получу какой-нибудь объект menuManager, который удовлетворяет IMenuManager. Но я должен убедиться, что все эти объекты действительно существуют, даже в других модулях. Я просто сказал, что контейнер тоже будет черным ящиком, так как, например, Я помню, как видел демонстрации Prism (StockTrader), где я закомментировал строку AddModule, и приложение все еще работало, но без одной области экрана, где бы этот модуль был. Но если другие модули ссылаются на объекты в этом модуле, приложение сломается. - person Edward Tanguay; 20.07.2009
comment
Это не связь, это конфигурация. Когда вы регистрируете класс, удовлетворяющий заданному интерфейсу, вы настраиваете систему для своих текущих потребностей. Но вы не запекаете зависимости в самих компонентах (они знают только об интерфейсах). И даже компонент Resolve<SomeClass> все еще может использовать внешнюю конфигурацию для управления тем, что возвращается. - person Daniel Earwicker; 20.07.2009
comment
Один вариант - это NullPattern, другой - использовать зависимости свойств для необязательного импорта, поскольку свойства всегда являются необязательными. В MEF мы разрешаем вам иметь дополнительный импорт, для которого установлено значение Null. Возможно, кому-то стоит опубликовать на форумах Unity предложение о поддержке дополнительных зависимостей. - person Glenn Block; 13.09.2009

Причина, по которой это работает, заключается в том, что Unity разработан для этого. Когда вы разрешаете конкретный тип, Unity проверяет, может ли он разрешиться из контейнера. Если нет, то он просто создает экземпляр типа, разрешающий его зависимости. Это действительно очень просто.

person Glenn Block    schedule 12.09.2009