代码之家  ›  专栏  ›  技术社区  ›  Martin Hansen Lennox

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

  •  0
  • Martin Hansen Lennox  · 技术社区  · 8 年前

    给定这些接口和类…

    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; }
    }
    

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

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

    “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)
    {
    }
    

    基本上,我不明白为什么 IEnumerable<IPage> 不匹配的返回类型 IEnumerable<AboutPage> .

    我想创建一个方法 IPageAdminViewModel<IPage> 作为论据我想知道我怎样才能 AboutPageAdminViewModel 符合这个要求。

    4 回复  |  直到 8 年前
        1
  •  2
  •   InBetween    8 年前

    学习埃里克的例子:

    class Animal {}
    class Tiger : Animal {}
    
    interface ICage { 
        Animal GetAnimal(); }
    
    class TigerCage : ICage { 
        //Won’t compile
        public Tiger GetAnimal() => 
            new Tiger(); }
    

    可以通过同时实现显式接口成员和强类型方法来解决此问题,其中一个方法将委托给另一个方法以避免代码重复:

    class TigerCage: ICage {
        Animal ICage.GetAnimal() => GetAnimal();
        public Tiger GetAnimal() => new Tiger(); }
    
        2
  •  8
  •   Eric Lippert    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(); }
    

    这是完全安全的。 ICage 要求实现者具有返回动物的方法,并且 TigerCage 是;它返回一个 Tiger 这是一个 Animal 是的。这是安全的,但不合法。

    C++具有这个特性。C#没有。

    人们对这个功能的要求已经超过15年了;你可以在这个网站上搜索“c return type covariance”,你会发现很多关于这个的答案(我和其他人)。

    它从未被实现过,因为clr本身不支持它,使用显式接口实现和隐藏有简单的解决方法,并且它将新的脆弱的基类失败引入到生态系统中。由于这些和其他原因,与其他对工作更有价值的特性相比,它一直是编译器团队的低优先级。

    如果你想要的话,可以在github论坛上支持它;我相信你会有公司的,但我也不会屏息等待编译器团队实现它;我们已经等了很长时间了!

    顺便说一句,从来没有人要求过 虚拟参数类型反方差 --也就是说,用带动物的方法替代带老虎的方法。但它同样安全。

        3
  •  1
  •   John Wu    8 年前

    问题不在于它是一种具体的类型。问题是它们必须匹配。

    两者都将编译:

    public class AboutPageAdminViewModel
        : PageAdminViewModel<IPage>
        , IPageAdminViewModel<IPage> 
    {
    }
    
    
    public class AboutPageAdminViewModel
        : PageAdminViewModel<AboutPage>
        , IPageAdminViewModel<AboutPage> 
    {
    }
    

    如果不匹配,全班就得想办法 Pages 同时返回 IEnumerable<IPage> 还有一个 IEnumerable<AboutPage> ,这是不可能的。它们之间有一种协变关系,但它们不是一回事。

        4
  •  1
  •   Amit    8 年前

    IPageAdminViewModel<IPage> 需要实现 IEnumerable<IPage> Pages { get; } ,而您的第一个示例“only”实现 IEnumerable<AboutPage> Pages { get; } .

    IEnumerable 是同变的,并不意味着它们是相同的,有一个不足以满足另一个的要求。

    你的 <out T> 只做 IPageAdminViewModel coavariant,也就是说,可以将类型的实例赋给用基泛型类型定义的变量/属性。