假设我有一个名为
StuffBase
的抽象类,其抽象属性类型为 IEnumerable
:
abstract class StuffBase
{
public abstract IEnumerable SomeStuff { get; set; }
}
现在,是否可以定义一个派生类,用
衍生类型覆盖
SomeStuff
属性,例如:
class StuffDerived : StuffBase
{
public override IEnumerable<int> SomeStuff { get; set; }
}
这个想法是
IEnumerable<int>
源自 IEnumerable
。有什么办法可以实现这样的目标吗?当我当前尝试此操作时,它给我错误“StuffDerived 未实现抽象成员 StuffBase.SomeStuff.Set”。
但是我不太明白:如果抽象类只定义了getter,但没有setter,那么它可以工作。例如,如果
SomeStuff
定义为
public abstract IEnumerable SomeStuff { get; }
和
public override IEnumerable<int> SomeStuff { get; }
它工作得很好。对此的解释也很好。
该功能称为 协变返回类型,在 C# 9 中引入:
支持协变返回类型。具体来说,允许重写方法来声明比它所重写的方法更派生的返回类型,并且类似地允许重写只读属性来声明更派生的类型。出现在更多派生类型中的重写声明需要提供至少与其基类型中的重写中出现的返回类型一样具体的返回类型。方法或属性的调用者将从调用中静态接收更精确的返回类型。
请注意,如果 setter 按照您的预期工作,则会违反班级合同。 IE。考虑以下几点:
var foo = new StuffDerived()
StuffBase bar = foo;
bar.SomeStuff = new List<object> {"hahah"};
int i = foo.First(); // boom
您可以使用泛型在某种程度上“解决”setter 问题:
abstract class StuffBase<T>
{
public abstract IEnumerable<T> SomeStuff { get; set; }
}
class StuffDerived : StuffBase<int>
{
public override IEnumerable<int> SomeStuff { get; set; }
}
但它有一些限制,例如
StuffDerived
不是 StuffBased<object>
(即 StuffBase<object> foo = new StuffDerived();
无法编译)。您可以使用一些基本接口和显式接口实现来解决更多问题,但同样没有设置器:
interface IStuff
{
IEnumerable SomeStuff { get; }
}
abstract class StuffBase<T> : IStuff
{
IEnumerable IStuff.SomeStuff => SomeStuff;
public abstract IEnumerable<T> SomeStuff { get; set; }
}
实际上,一些类似的问题与 C# 中的“普通”差异相关,因此您也可以阅读: