Here is what I do:
First, I define theses interfaces:
public interface IView<TPresenter>
{
TPresenter Presenter { get; set; }
}
public interface IPresenter<TView, TPresenter>
where TView : IView<TPresenter>
where TPresenter : IPresenter<TView, TPresenter>
{
TView View { get; set; }
}
Then this abstract presenter class:
public abstract class AbstractPresenter<TView, TPresenter> : IPresenter<TView, TPresenter>
where TView : IView<TPresenter>
where TPresenter : class, IPresenter<TView, TPresenter>
{
protected TView view;
public TView View
{
get { return this.view; }
set
{
this.view = value;
this.view.Presenter = this as TPresenter;
}
}
}
The view is injected via a property, instead of the constructor, to allow the bi-directional affection in the setter. Notice that a safe cast is needed...
Then, my concrete presenter is something like :
public class MyPresenter : AbstractPresenter<IMyView, MyPresenter>
{
//...
}
Where IMyView
implements IView
. A concrete view type must exists (e.g. MyView
), but it's the container that resolves it:
- I register
MyPresenter
type as itself in the container, with a transient behavior.
- I register
MyView
as an IMyView
in the container with a transient behavior.
- I then asks for a
MyPresenter
to the container.
- Container instanciate a MyView
- It instanciates a
MyPresenter
- It inject the view into the presenter through the
AbstractPresenter.View
property.
- The setter code completes the bi-directional association
- The container returns the couple Presenter/View
It allows you to inject other dependencies (services, repos) into both your view and your presenter. But in the scenario you described, I recommend you to inject services and caches into the presenter, instead of the view.