为何重写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,但按照父类的约定这应该是允许的,因此编译器禁止这种重写。
解决方案
改为只读属性(推荐)
如果不需要修改Values的赋值,将属性改为只读,此时就能支持返回类型协变:class Foo { public virtual IEnumerable<X> Values { get; } } class Bar : Foo { public override A Values { get; } }保留可写属性的替代方案
如果必须保留set操作,无法使用返回类型协变,可以选择:- 保持子类
Values的类型与父类一致,即仍为IEnumerable<X>; - 使用
new关键字隐藏父类属性(注意这不是重写,只是定义同名成员):class Bar : Foo { public new A Values { get; set; } }
- 保持子类
内容的提问来源于stack exchange,提问作者romato
相关产品推荐
相关产品推荐

