抽象属性可以接受在重写属性中派生的类型

huangapple go评论110阅读模式
英文:

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&lt;int&gt; SomeStuff { get; set; }
}

The idea is that IEnumerable&lt;int&gt; 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&lt;int&gt; 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&lt;object&gt; {&quot;hahah&quot;};
int i = foo.First(); // boom

You can use generics to "workaround" to some extent the setter problem:

abstract class StuffBase&lt;T&gt;
{
	public abstract IEnumerable&lt;T&gt; SomeStuff { get; set; }
}

class StuffDerived : StuffBase&lt;int&gt;
{
	public override IEnumerable&lt;int&gt; SomeStuff { get; set; }
}

But it has some limitations like StuffDerived is not StuffBased&lt;object&gt; (i.e. StuffBase&lt;object&gt; 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&lt;T&gt; : IStuff
{
	IEnumerable IStuff.SomeStuff =&gt; SomeStuff;
	public abstract IEnumerable&lt;T&gt; SomeStuff { get; set; }
}

Actually some similar "problems" a relevant to "ordinary" variance in C# so you can read also:

huangapple
  • 本文由 发表于 2023年8月9日 10:06:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/76864143-2.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定