I am trying to create the following classes.
But I am having struggling to get a couple of View Model classes to implement interfaces, as detailed below.
Data Model Classes:
public interface IPage
{
string PageTitle { get; set; }
string PageContent { get; set; }
}
public interface IDatedPage
: IPage
{
DateTime PageDate { get; set; }
}
public abstract class Page
: IPage
{
public string PageTitle { get; set; }
public string PageContent { get; set; }
}
public abstract class DatedPage
: Page
, IDatedPage
{
public DateTime PageDate { get; set; }
}
public class AboutPage
: Page
, IPage
{
}
public class NewsPage
: DatedPage
, IDatedPage
{
}
Related View Model Classes:
public interface IPageAdminViewModel<T>
where T : IPage
{
IPagedList<T> Pages { get; set; }
}
public interface IDatedPageAdminViewModel<T>
: IPageAdminViewModel<T>
where T : IDatedPage
{
}
public abstract class PageAdminViewModel<T>
: IPageAdminViewModel<T>
where T: IPage
{
public IPagedList<T> Pages { get; set; }
}
public abstract class DatedPageAdminViewModel<T>
: PageAdminViewModel<T>
, IDatedPageAdminViewModel<T>
where T : IDatedPage
{
}
public class AboutPageAdminViewModel
: PageAdminViewModel<AboutPage>
, IPageAdminViewModel<IPage>
{
}
public class NewsPageAdminViewModel
: DatedPageAdminViewModel<NewsPage>
, IDatedPageAdminViewModel<IDatedPage>
{
}
The Problem
I can't get AboutPageAdminViewModel
to implement IPageAdminViewModel<IPage>
.
Similarly NewsPageAdminViewModel
won't implement IDatedPageAdminViewModel<IDatedPage>
.
'AboutPageAdminViewModel' does not implement interface member 'IPageAdminViewModel<IPage>.Pages'. 'PageAdminViewModel<AboutPage>.Pages' cannot implement 'IPageAdminViewModel<IPage>.Pages' because it does not have the matching return type of 'IPagedList<IPage>'.
What I've Tried 1:
I have tried adding out
to the generic parameter on the interface definition, like this:
public interface IPageAdminViewModel<out T>
where T : IPage
{
IPagedList<T> Pages { get; set; }
}
public interface IDatedPageAdminViewModel<out T>
: IPageAdminViewModel<T>
where T : IDatedPage
{
}
But it gives me the error:
Invalid variance: The type parameter 'T' must be invariantly valid on 'IPageAdminViewModel.Pages'. 'T' is covariant.
It won't let me add out
to the .Pages
property:
Invalid variance modifier. Only interface and delegate type parameters can be specified as variant.
What I've Tried 2:
I've learned that the out
parameter doesn't play nicely with List<>
, so I tried changing all IPagedList
properties to IEnumerable
. But I am still getting;
'AboutPageAdminViewModel' does not implement interface member 'IPageAdminViewModel<IPage>.Pages'. 'PageAdminViewModel<AboutPage>.Pages' cannot implement 'IPageAdminViewModel<IPage>.Pages' because it does not have the matching return type of 'IEnumerable<IPage>'.
Update
I'm trying to break it down into smaller questions of which this is the first:
Generic parameter - using a concrete type compiles, but an implemented interface does not
public class AboutPageAdminViewModel
: PageAdminViewModel<AboutPage>
, IPageAdminViewModel<IPage>
{
}
The two constraints you have on the class contradict one another. PageAdminViewModel<AboutPage>
says that the Pages
collection returns a set of AboutPage
s and will accept only an AboutPage
. While, IPageAdminViewModel<IPage>
says that Pages
returns any IPage
and can take any IPage
too. So while one constraint guarantees only AboutPage
the other constraint opens the door to any IPage
. In short not allowed, if it were allowed, you could get a ProfilePage
out of the Pages
collection simply because it too was an IPage
. The only way you can get both behaviors is by implementing each Pages
collection separately.