通用参数 - 使用具体类型编译,但实现的接口不编译

Mar*_*nox 0 c# generics interface

鉴于这些接口和类......

public interface IPage
{
    string PageTitle { get; set; }
    string PageContent { get; set; }
}


public abstract class Page
    : IPage
{
    public string PageTitle { get; set; }
    public string PageContent { get; set; }
}


public class AboutPage
    : Page
    , IPage
{
}


public interface IPageAdminViewModel<out T>
    where T : IPage
{
    IEnumerable<T> Pages { get; }
}


public abstract class PageAdminViewModel<T>
    : IPageAdminViewModel<T>
    where T: IPage
{
    public IEnumerable<T> Pages { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

为什么使用IPage作为接口类型参数不编译....

public class AboutPageAdminViewModel
    : PageAdminViewModel<AboutPage>
    , IPageAdminViewModel<IPage> // ERROR HERE - I realise this declaration is not required, just indicating where the error is (not)
{
}
Run Code Online (Sandbox Code Playgroud)

'AboutPageAdminViewModel'未实现接口成员'IPageAdminViewModel <IPage> .Pages'.'PageAdminViewModel <AboutPage> .Pages'无法实现'IPageAdminViewModel <IPage> .Pages',因为它没有匹配的返回类型'IEnumerable <IPage>'.

....使用具体课时AboutPage呢?

public class AboutPageAdminViewModel
    : PageAdminViewModel<AboutPage>
    , IPageAdminViewModel<AboutPage> // NO ERROR - I realise this declaration is not required, just indicating where the error is (not)
{
}
Run Code Online (Sandbox Code Playgroud)

从本质上讲,我不明白为什么返回类型与返回类型IEnumerable<IPage>不匹配IEnumerable<AboutPage>.

我想创建一个IPageAdminViewModel<IPage>以参数为例的方法.我想知道如何/如果我能够AboutPageAdminViewModel符合这个要求.

Eri*_*ert 8

从本质上讲,我不明白为什么返回类型与返回类型IEnumerable<IPage>不匹配IEnumerable<AboutPage>.

首先:您是正确的,财产符合IEnumerable<IPage>合同的所有要求.允许这样做对C#来说是类型安全的.

您想要的功能称为虚拟返回类型协方差,C#不支持它.您可以通过比您更简单的示例看到这一点:

class Animal {}
class Tiger : Animal {}
interface ICage { Animal GetAnimal(); }
class TigerCage : ICage { public Tiger GetAnimal() => new Tiger(); }
Run Code Online (Sandbox Code Playgroud)

这是完全安全的. ICage要求实施者有一个返回动物的方法,并且TigerCage; 它返回一个TigerAnimal.这是安全的,但不合法.

C++具有此功能.C#没有.

人们一直在要求这个功能15年以上; 你可以在这个网站上搜索"C#return type covariance",你会发现很多答案(由我和其他人).

它从未被实现,因为CLR本身不支持它,使用显式接口实现和阴影有简单的解决方法,并且它将新的脆弱基类故障引入生态系统.由于这些原因和其他原因,与其他更有价值的功能相比,编译器团队一直是低优先级.

如果你想要它,请在GitHub论坛上提倡它; 我相信你会有合作,但我也不会屏息等待编译团队实施它; 我们已经等了很久了!

顺便说一句,没有人要求虚拟参数类型的逆转 - 也就是说,覆盖一个方法,该方法将Tiger带有一个带动物的方法.但它同样安全.