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

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

Abstract property that can accept a derived type in the overridden property

问题

假设我有一个名为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 does not implement abstract member StuffBase.SomeStuff.Set"**。

但是这里有一点我不太明白:如果抽象类只定义了一个getter,而没有定义setter,那么它是可以工作的。例如,如果SomeStuff被定义为:

public abstract IEnumerable SomeStuff { get; }

public override IEnumerable<int> SomeStuff { get; }

它可以完美地工作。对此的解释也会很好。

英文:

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

这个功能被称为协变返回类型,并在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不是StuffBase<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.html
匿名

发表评论

匿名网友

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

确定