覆盖 get 方法,但不覆盖 set 方法

93

我有一个抽象类,它定义了一个get,但没有set,因为就该抽象类而言,它只需要get

public abstract BaseClass
{
  public abstract double MyPop
  {get;}
}

但是在某些派生类中,我需要一个 set 属性,因此我正在查看这个实现。

public class DClass: BaseClass
{
  public override double MyPop
  {get;set;}
}

问题是,我遇到一个编译错误,显示:

 

*.set:无法覆盖因为*.没有可重写的设置访问器。

尽管我认为上述语法完全合法。

您对此有什么想法? 有解决方法吗,或者为什么会这样?

编辑:我唯一能想到的方法是将getset都放在抽象类中,并让子类在调用不必要的set时抛出NotImplementedException。 这是我不喜欢的方法,特别是特殊的setter方法


让我们稍微分解一下这个问题。 您需要构建一组类,公开一个读取double值的方法,该方法将在每个类中具体实现。有时需要设置此值,因此其中一些类应公开一种设置方式。这样对吗?您需要使用多少级继承? - Codesleuth
1
@Codesleuth:是的。至于继承的层数,我不确定这与问题有什么关系? - Graviton
@David,具有讽刺意味的是,这似乎是一个我不想接受答案的问题。 - Graviton
3
为什么接口可以做到这一点,而抽象类却不行,我不太理解。是什么原因导致的? - BlueRaja - Danny Pflughoeft
这太糟糕了。有人能理解为什么这不可能吗?get和set属性在编译过程中确实被翻译成方法等价物,对吧?所以这些方法的虚拟性可能是孤立的问题?编辑:我想也许我在这里找到了答案:https://dev59.com/Z3VD5IYBdhLWcg3wHnwX - Mattias Nordqvist
请注意,此功能自2016年以来一直在考虑中(请参见https://github.com/dotnet/roslyn/issues/9482)。 它目前是一个[积压功能](https://github.com/dotnet/roslyn/wiki/Labels-used-for-issues)。 - Brian
13个回答

28

一种可能的解决方案是覆盖getter,然后实现一个单独的setter方法。如果你不想在基类中定义属性setter,那么你没有太多其他选项。

public override double MyPop
{
    get { return _myPop; }
}

public void SetMyPop(double value)
{
    _myPop = value;
}

21

C# 6.0的新功能:

如果你在构造函数中只调用setter方法,你可以使用只读属性来解决这个问题。

void Main()
{
    BaseClass demo = new DClass(3.6);
}

public abstract class BaseClass
{
    public abstract double MyPop{ get; }
}

public class DClass : BaseClass
{
    public override double MyPop { get; }
    public DClass(double myPop) { MyPop = myPop;}
}

6
虽然这是真实的,但这并不是解决问题的一个好的通用方案。当然会有很多情况需要公共的设置器。 - Tom Bogle

17

你想要的是不可能实现的。你必须在抽象属性中定义setter,否则你将无法正确地重写它。

我所知道的唯一情况是通过使用接口来定义getter并实现getter/setter:

public interface IBaseInterface
{
    double MyPop { get; }
}

public class DClass : IBaseInterface
{
    public double MyPop { get; set; }
}

13
不错的方法;但有时候你只需要使用抽象类,而不是接口。 - Graviton
Graviton的担忧在C# 8中得到了部分缓解,因为它引入了默认接口方法 - Brian

10
如果BaseClass在你自己的代码库中,那么你可以这样做:
abstract public class BaseClass
{
    abstract public double MyPop { get; protected set; }
}

public class DClass : BaseClass
{
    private double _myProp;
    public override double MyProp
    {
        get { return _myProp; }
        protected set { _myProp = value; }
    }
}

编辑:你可以在DClass中创建一个公共方法 SetMyProp(double myProp) 或类似方法。你的领域模型的类设计应该清楚地表明或说明为什么不能直接在基类中设置属性,以及为什么可以在派生类中这样做。


1
好的,但是那些不需要设置 MyPop 的子类怎么办? - Graviton
也许它们只能从BaseClass派生,而不能从DClass派生? - herzmeister
我认为这对我的应用程序不是一个好主意;继承应该保留给“是一个”关系,而不是为了解决语言限制;此外,我在这里的应用程序中利用继承进行其他更现实的目的。 - Graviton
1
我从来没有遇到过这个问题,而且我对OO的正确性非常狂热,相信我;-)...(尽管OO有局限性,请注意)...也许当你告诉我关于你的确切情况时,我可以就你的模型提出建议。 - herzmeister
1
我在考虑一个真实世界的例子,我想看看你想要建模的东西。例如,你的动物、长颈鹿和狮子类,或者你的天体、流星和行星类。 - herzmeister
显示剩余2条评论

5

如果你找到一种方法来做你想做的事情,你确定这是一个好的设计吗?

这将允许子类的对象进行状态更改,而父类的对象无法进行。这不会违反Liskov替换原则吗?


2
你可以这样做:
抽象类 TestBase { public 抽象属性 Int { 获取; } }
class TestDerivedHelper : TestBase
{
    private int _Int;
    public override int Int
    {
        get
        {
            return _Int;
        }
    }

    protected void SetInt(int value)
    {
        this._Int = value;
    }
}

class TestDerived : TestDerivedHelper
{
    public new int Int
    {
        get { return base.Int; }
        set { base.SetInt(value); }
    }
}

使用TestDerived将具备您所需的功能。我唯一能看到的缺点是,您必须在TestDerivedHelper中实现每个抽象方法,但这样可以让您在以后拥有更多的控制权。
希望这可以帮助您。;)

2
这不可能的原因是C#语言中参数的“神奇”创建方式。当你定义一个参数时,C#会创建一个私有字段,由隐式的getter和setter进行操作。如果基类中没有setter,那么无法从子类中编写的方法更改此变量(因为private标识禁止子类甚至访问它)。通常情况下,它会使用基类的隐式setter。
如果不是所有的子类都可以这样做,我不建议将set放在基类中,因为这违反了多态编程的整体原则(抽象类中定义的任何抽象方法必须由子类实现)。创建一个特殊的setter方法,就像其他答案中描述的那样,可能是最好的方式。

1
即使这个线程很久远,我还是会发布我的解决方案,以便帮助他人。这不是我自己的方案,而是基于其他SO主题中的答案。
public abstract BaseClass
{
  public double MyPoP { get { return GetMyPoP; } }

  protected abstract double GetMyPoP { get; }
}

public class DClass: BaseClass
{
  public new double MyPoP { get; set; }

  protected override double GetMyPop { get { return MyPoP; } }
}

这种解决方案为每个需要修改访问器的属性添加了一行额外的代码。然而,对外部可见性没有任何变化,并提供所需的功能。

1
围攻
abstract class TestBase
{
    public abstract int Int { get; }
}

class TestDerivedHelper : TestBase
{
    private int _Int;
    public override int Int
    {
        get
        {
            return _Int;
        }
    }

    protected void SetInt(int value)
    {
        this._Int = value;
    }
}

class TestDerived : TestDerivedHelper
{
    public new int Int
    {
        get { return base.Int; }
        set { base.SetInt(value); }
    }
}

使用TestDerived将具有您正在寻找的功能。我唯一能看到的缺点是,您必须在TestDerivedHelper中实现每个抽象方法,但它可以在以后给您更多控制。 我使用这种方法,对我非常有效。 此外,我也将我的“TestDerivedHelper”类设为抽象类,然后所有方法都必须在“TestDerived”类中实现。

0
public abstract class BaseClass
{
    public abstract double MyPop { get; }
}

public class DClass: BaseClass
{
    private double _myPop = 0;
    public override double MyPop 
    {
        get { return _myPop; }
    }

    // some other methods here that use the _myPop field
}

如果您需要从外部设置属性DClass,那么将setter放入基类可能会更好。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接