英文:
Abstract property that can accept a derived type in the overridden property
问题
以下是您要翻译的代码部分:
Let's say I have an abstract class called `StuffBase` with an abstract property of type `IEnumerable`:
abstract class StuffBase
{
public abstract IEnumerable SomeStuff { get; set; }
}
Now, is it possible to define a derived class that overrides the `SomeStuff` property with a *derived* type, like, for instance:
class StuffDerived : StuffBase
{
public override IEnumerable<int> SomeStuff { get; set; }
}
The idea is that `IEnumerable<int>` is derived from `IEnumerable`. Is there any way to achieve something like this? When I currently try this, it gives me the error **"StuffDerived does not implement abstract member StuffBase.SomeStuff.Set"**.
**But here's what I don't quite get:** If the abstract class only defines a **getter**, but *not* a **setter**, then *it works*. For instance, if `SomeStuff` is defined as
public abstract IEnumerable SomeStuff { get; }
and
public override IEnumerable<int> SomeStuff { get; }
it works perfectly fine. An explanation for this would also be nice.
英文:
Let's say I have an abstract class called StuffBase
with an abstract property of type IEnumerable
:
abstract class StuffBase
{
public abstract IEnumerable SomeStuff { get; set; }
}
Now, is it possible to define a derived class that overrides the SomeStuff
property with a derived type, like, for instance:
class StuffDerived : StuffBase
{
public override IEnumerable<int> SomeStuff { get; set; }
}
The idea is that IEnumerable<int>
is derived from IEnumerable
. Is there any way to achieve something like this? When I currently try this, it gives me the error "StuffDerived does not implement abstract member StuffBase.SomeStuff.Set".
But here's what I don't quite get: If the abstract class only defines a getter, but not a setter, then it works. For instance, if SomeStuff
is defined as
public abstract IEnumerable SomeStuff { get; }
and
public override IEnumerable<int> SomeStuff { get; }
it works perfectly fine. An explanation for this would also be nice.
答案1
得分: 1
该功能称为covariant return types,并在C# 9中引入:
> 支持协变返回类型。具体来说,允许覆盖方法声明比其覆盖的方法更派生的返回类型,类似地,允许覆盖只读属性声明更派生的类型。出现在更派生类型中的覆盖声明将需要提供至少与其基类型中的覆盖中出现的返回类型一样具体的返回类型。方法或属性的调用者将在调用时从更精确的返回类型中静态地接收更精细化的返回类型。
请注意,如果setter的工作方式与您所期望的方式相同,它将违反类的约定。例如,考虑以下情况:
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();
将无法编译)。您可以使用一些基础接口和显式接口实现来更进一步,但仍然无法使用setter:
interface IStuff
{
IEnumerable SomeStuff { get; }
}
abstract class StuffBase<T> : IStuff
{
IEnumerable IStuff.SomeStuff => SomeStuff;
public abstract IEnumerable<T> SomeStuff { get; set; }
}
实际上,一些类似的“问题”与C#中的“普通”协变性相关,因此您还可以阅读以下内容:
英文:
The feature is called covariant return types and was introduced in C# 9:
> Support covariant return types. Specifically, permit the override of a method to declare a more derived return type than the method it overrides, and similarly to permit the override of a read-only property to declare a more derived type. Override declarations appearing in more derived types would be required to provide a return type at least as specific as that appearing in overrides in its base types. Callers of the method or property would statically receive the more refined return type from an invocation.
Note that if the setter would work like you have desired it would break the class contract. I.e. consider the following:
var foo = new StuffDerived()
StuffBase bar = foo;
bar.SomeStuff = new List<object> {"hahah"};
int i = foo.First(); // boom
You can use generics to "workaround" to some extent the setter problem:
abstract class StuffBase<T>
{
public abstract IEnumerable<T> SomeStuff { get; set; }
}
class StuffDerived : StuffBase<int>
{
public override IEnumerable<int> SomeStuff { get; set; }
}
But it has some limitations like StuffDerived
is not StuffBased<object>
(i.e. StuffBase<object> foo = new StuffDerived();
will not compile). You can workaround a bit more with some base interface and explicit interface implementation, but again with no setter:
interface IStuff
{
IEnumerable SomeStuff { get; }
}
abstract class StuffBase<T> : IStuff
{
IEnumerable IStuff.SomeStuff => SomeStuff;
public abstract IEnumerable<T> SomeStuff { get; set; }
}
Actually some similar "problems" a relevant to "ordinary" variance in C# so you can read also:
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论