You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何重写IEnumerable<T>属性时返回类型协变报错?

问题描述

我定义了以下C#类:

class X 
{
    public X() {}
}

class A : IEnumerable<X>
{
    public IEnumerator<X> GetEnumerator()
    {
        yield return new X();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

class Foo
{
    public virtual IEnumerable<X> Values { get; set; }
}

class Bar : Foo
{
    public override A Values { get; set; }
}

Foo类包含一个类型为IEnumerable<X>的虚拟属性Values,派生类Bar尝试用实现了IEnumerable<X>的类A重写该属性,预期支持返回类型协变,却遇到错误:

'Bar.Values': type must be 'IEnumerable' to match overriden member 'Foo.Values'

明明类A已实现IEnumerable<X>,为何该重写类型不生效?

问题原因与解决方案

核心原因

C#的返回类型协变仅支持只读成员(比如只有get的属性、无参数方法),而你定义的Values是可读可写属性(包含get和set),这种场景下协变无法生效:

  • 对于get操作,返回A(派生自IEnumerable<X>)是符合协变规则的;但对于set操作,父类约定可以接受任意IEnumerable<X>类型的实例(比如List<X>),而子类Bar.Values要求只能赋值A类型,这会破坏类型安全——你无法把List<X>赋值给Bar.Values,但按照父类的约定这应该是允许的,因此编译器禁止这种重写。

解决方案

  1. 改为只读属性(推荐)
    如果不需要修改Values的赋值,将属性改为只读,此时就能支持返回类型协变:

    class Foo
    {
        public virtual IEnumerable<X> Values { get; }
    }
    
    class Bar : Foo
    {
        public override A Values { get; }
    }
    
  2. 保留可写属性的替代方案
    如果必须保留set操作,无法使用返回类型协变,可以选择:

    • 保持子类Values的类型与父类一致,即仍为IEnumerable<X>;
    • 使用new关键字隐藏父类属性(注意这不是重写,只是定义同名成员):
      class Bar : Foo
      {
          public new A Values { get; set; }
      }
      

内容的提问来源于stack exchange,提问作者romato

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 18:16:04