Background:
I have three Silverlight Pages, that implement my interface:
interface IPageWithData<in T> where T : Entity
{
void SetData(T obj);
}
public class APage : Page, IPageWithData<AItem> { /* Implementation */ }
public class BPage : Page, IPageWithData<BItem> { /* Implementation */ }
public class CPage : Page, IPageWithData<CItem> { /* Implementation */ }
// AItem, BItem and CItem are (EF) Entities (they are derived from Entity class).
Then I use a custom manager that returns a page that relates to the treeview node given as a parameter:
private static class PageContainerHelper
{
public static IPageWithData<Entity> SetPage(RadTreeViewItem selectedNode)
{
APageRTVI aNode = selectedNode as APageRTVI;
BPageRTVI bNode = selectedNode as BPageRTVI;
CPageRTVI cNode = selectedNode as CPageRTVI;
if (aNode != null)
{
if (APage == null)
APage = new APage();
APage.SetData(aNode.aItem);
return APage; // ERROR HERE
}
/*
... the same for BPageRTVI and CPageRTVI
*/
}
public static APage APage { get; set; }
public static BPage BPage { get; set; }
public static CPage CPage { get; set; }
}
Problem:
I seem to be understanding co/contravariance the wrong way.
The SetPage()
method doesn't allow me to return APage
:
Cannot implicitly convert type 'MyProject.Views.Pages.APage' to 'MyProject.Views.Pages.IPageWithData'. An explicit conversion exists (are you missing a cast?)
APage
implements IPageWithData<T>
(albeit T
is of a more derived type). Why does this require a explicit conversion? And would an explicit conversion even work here?
So basically, there are two things I need. One is that when SetPage
is returning a page, it can use a page that implements IPageWithData
of a type that is more derived than Entity
. And the other thing I need is for the IPageWithData<T>.SetData
method to be able to receive arguments that are of a type more derived than a T
, that is Entity
.
Is this possible?
Consider what you can do with IPageWithData<Entity>
. You can do:
IPageWithData<Entity> pageWithData = PageContainerHelper.SetPage(...);
BItem item = new BItem();
pageWithData.SetData(item);
Now the implementation is an APage
- what would you want it to be able to do with a BItem
? I suspect you don't want that.
In other words, the compiler is doing exactly what it should - it's protecting you from sending the wrong kind of data to a page which can't handle it. Quite how you want to fix this, I don't really know - I suspect you want SetPage
to be generic too:
public static IPageWithData<T> SetPage<T>(RadTreeViewItem selectedNode)
where T : Entity
but it's not clear to me what that does to your implementation.