Search code examples
c#genericsinterfacecastingcovariance

C# casting an inherited Generic interface


I'm having some trouble getting my head around casting an interface I've come up with. It's an MVP design for C# Windows Forms. I have an IView class which I implement on my form classes. There's also an IPresenter which I derive into various specific Presenters. Each Presenter will manage the IView differently depending on the role, for example opening the dialog to enter a new set of data with an AddPresenter as opposed to editing existing data with an EditPresenter which would preload data onto the form. Each of these inherit from IPresenter. I want to use the code as such:

AddPresenter<ConcreteView> pres = new AddPresenter<ConcreteView>();

I basically have this working but these presenters and the views they manage are bundled into plugins which are loaded post runtime which means I need a Manager class which acts as the plugin interface take a "mode" parameter. This mode parameter is use for a factory method to create either the Add or Edit Presenter, but because the call to show the dialog is made later on then I need to make the call via the IPresenter interface like so:

private IPresenter<IView> pres;
public ShowTheForm()
{
    pres.ShowDialog();
}

Now I'm having issues when it comes to casing my concrete instantiation of an AddPresenter say, to the 'pres' member. Here's a cut down simplified version of what I have:

interface IView
{
    void ViewBlah();
}

interface IPresenter<V> where V : IView
{
    void PresBlah();
}

class CView : IView
{
    public void ViewBlah()
    {        
    }
}

class CPresenter<T> : IPresenter<T> where T : IView
{
    public void PresBlah()
    {
    }
}

private void button3_Click(object sender, EventArgs e)
{
    CPresenter<CView> cpres = new CPresenter<CView>();
    IPresenter<IView> ipres = (IPresenter<IView>)cpres;
}

This is the error:

Unable to cast object of type 'CPresenter`1[MvpApp1.MainForm+CView]' to type 'IPresenter`1[MvpApp1.MainForm+IView]'.

Both the Presenter and the Generic type specification from what I can tell ARE subclasses of the interfaces so I can't understand why it won't cast.

Any thoughts?

Steve


Solution

  • The problem is the generic type parameter. If you make the interface parameter covariant then the cast will work.

    This is accomplished by adding the out keyword, like so:

    interface IPresenter<out V> where V : IView
    {
        void PresBlah();
    
    }
    

    You can learn more about how this works with the following MSDN article: Covariance and Contravariance in Generics. The section Generic Interfaces with Covariant Type Parameters specifically applies to your question.

    Update: Make sure you check the comments between @phoog and me. If your actual code accepts a V as an input, you will be unable to make it covariant. The referenced article and @phoog's answer explains this case in further detail.