覆盖获取,但未设置


78

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

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

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

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

问题是,我收到一个编译错误,说

* .set:无法覆盖,因为*。没有可覆盖的set访问器。

即使我认为上述语法完全合法。

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

编辑:我能想到的唯一方法是将get和都放在set抽象类中,然后让子类引发NotImplementedExceptionifset调用,这是不必要的。我不喜欢这种东西,还有一种特殊的设置方法


让我们分解一下。您需要构建一组类,这些类公开一个方法来读取一个double值,该值将在每个类中专门实现。有时需要设置此值,因此其中一些类应提供设置此值的方法。这是正确的吗?您需要使用多少级继承?
Codesleuth

1
@Codesleuth:是的。至于继承的等级有多少,我不确定这与问题有什么关系?
重力1998年

@David,具有讽刺意味的是,这似乎是一个我不想接受答案的问题。
Graviton 2010年

3
我不明白为什么这对于接口而不是抽象类是可行的。是什么赋予了?
BlueRaja-Danny Pflughoeft 2012年

太烂了 有谁知道为什么这不可能?将get和set属性确实转换为编译过程中某个地方的方法等效项,不是吗?那么这些方法的虚拟性可以孤立吗?编辑:我想也许我发现我的答案在这里:stackoverflow.com/questions/82437/...
马蒂亚斯Nordqvist

Answers:


15

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;}
}

4
尽管这是事实,但这也不是解决该问题的通用解决方案。当然,在很多情况下,都需要公共策划者。
汤姆·博格尔

26

一个可能的答案是重写getter,然后实现单独的setter方法。如果您不希望在基础中定义属性设置器,则没有很多其他选择。

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

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

17

无法做您想做的事。您必须在abstract属性中定义设置器,否则将无法正确覆盖它。

我知道定义getter和实现getter / setter的唯一情况是使用接口:

public interface IBaseInterface
{
    double MyPop { get; }
}

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

12
不错的方法;但有时您只需要使用抽象类,而不是接口。
重力

@Graviton:我会注意到,如果/当C#引入Default Interface Methods时,此解决方案将变得更加可行。
Brian

8

如果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; }
    }
}

编辑:然后,您可以在DClassSetMyProp(double myProp)等中创建一个公共方法。您的域模型的类设计应该清楚或能说明为什么不能直接在基类中设置属性,以及为什么可以在派生类中设置属性。


好的,但是那些不需要设置MyPop?的子类呢?
Graviton 2010年

也许它们只能从BaseClass派生而不能从DClass派生?
herzmeister 2010年

我认为这对我的应用程序不是一个好主意;继承应该保留给is a关系,而不是为了避免语言限制;此外,我在这里的应用程序中将继承用于其他更现实的目的。
Graviton 2010年

1
我实际上从来没有遇到过这个问题,并且我对OO的正确性非常狂热,相信我;-) ...(尽管OO有局限性,请注意)...也许当您告诉我您可以提出的确切方案时关于模型的建议。
herzmeister

1
我想更多地考虑一个真实的例子,我想看看您想建模什么。例如,您的Animal,Giraffe和Lion类或CelestialBody,Meteor和Planet类。
herzmeister 2010年

5

您确定如果找到了一种可以做的方法,那么尝试做的事情将是一个好的设计吗?

它将允许子类的对象进行父类对象无法进行的状态更改。这不会违反《里斯科夫换人原则》吗?


2

您可以执行以下操作:

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中实现每个抽象方法,但是稍后它会为您提供更多控制。

希望这可以帮助。;)


2

之所以无法这样做,是因为C#将参数“魔化”为存在。定义参数时,C#将创建一个私有字段,供隐式getter和setter操作。如果基类中没有设置器,则无法通过子类中编写的方法更改此变量(因为私有标志甚至禁止子类访问它)。通常发生的情况是,它使用基类的隐式setter代替。

我不建议如果不是所有子类都可以将集合放在基类中,因为这违背了多态编程的整个原理(抽象类中定义的任何抽象方法都必须由子类实现)。如其他答案所述,创建一种特殊的setter方法可能是最好的方法。


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”类上实现。


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; } }
}

该解决方案为需要修改访问器的每个此类属性添加了一行额外的代码。但是,外部可见性没有变化,并且提供了所需的功能。


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放入基类中。


-2

编辑:

好的,我可能对此回应有些仓促,但现在我已经考虑了一下。

您是否必须使用抽象基类?如果不需要,请尝试以下操作:

public interface ISomeRelevantName
{
    double MyPop { get; }
}

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

2
会抱怨MyPopfrom的abstract属性BaseClass未在派生类中实现。
乔伊,2010年

嗯,我的回答使我感到羞愧:(我试图用寿编辑纠正这一点。
Codesleuth

-3

为什么不只在具有私有setter的基类中拥有一个属性,又为什么在需要setter的子类中拥有一个属性,将其覆盖并使其公开。


6
抽象访问者不能为私有
Graviton 2010年

一个abstract属性的getset访问者的private类似物protected。因此,在基类中您需要:public abstract MyProp { get; protected set; }
JoeBrockhaus

-3

您不能覆盖set访问器,因为基类未定义set访问器。

您可以使用new关键字隐藏基本类的实现,但这可能不是您想要的。


1
基类是抽象的,没有实现,因此new无法工作。
乔伊,2010年

需要说明的是:这不起作用,因为派生类未实现abstract属性。以上评论可能略有不正确。
乔伊,2010年

我的评论也具有误导性(建议“新”完全可以使用),所以我删除了它。不过感谢您的更新。
本杰明·波兹尊
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.