Überschreiben bekommen, aber nicht gesetzt

78

Ich habe eine abstrakte Klasse, die a definiert get, aber nicht set, weil diese abstrakte Klasse nur a benötigt get.

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

In einigen der abgeleiteten Klassen benötige ich jedoch eine setEigenschaft, daher sehe ich mir diese Implementierung an

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

Das Problem ist, dass ich einen Kompilierungsfehler habe, der das sagt

* .set: kann nicht überschrieben werden, weil *. hat keinen überschreibbaren Set-Accessor.

Auch wenn ich denke, dass die obige Syntax absolut legitim ist.

Irgendeine Idee dazu? Problemumgehung oder warum ist das so?

Bearbeiten: Der einzige Ansatz, den ich mir vorstellen kann, besteht darin, beide getund setwie in der abstrakten Klasse zu setzen und die Unterklasse ein NotImplementedExceptionif auslösen zu lassen, das setaufgerufen wird und nicht notwendig ist. Das mag ich nicht, zusammen mit einer speziellen Setter-Methode .

Graviton
quelle
Lassen Sie uns das ein wenig zusammenfassen. Sie müssen eine Reihe von Klassen erstellen, die eine Methode zum Lesen eines doubleWerts verfügbar machen, der speziell in jeder Klasse implementiert wird. Manchmal muss dieser Wert festgelegt werden, daher sollten einige dieser Klassen eine Möglichkeit zum Festlegen dieses Werts bereitstellen. Ist das richtig? Wie viele Vererbungsstufen müssen Sie verwenden?
Codesleuth
1
@ Codesleuth: ja. Ich bin mir nicht sicher, wie viele Vererbungsebenen für die Frage relevant sind.
Graviton
@ David, ironischerweise scheint dies eine Frage zu sein, auf die ich keine Antwort akzeptieren möchte.
Graviton
3
Ich verstehe nicht, warum dies für Schnittstellen möglich ist, aber nicht für abstrakte Klassen. Was gibt?
BlueRaja - Danny Pflughoeft
Das ist so blöd. Versteht jemand, warum dies nicht möglich ist? get- und set-Eigenschaften werden wirklich irgendwo entlang der Kompilierungsstraße in Methodenäquivalente übersetzt, nicht wahr? Die Virtualität dieser Methoden könnte also eine Einzelsache sein? edit: Ich denke, vielleicht habe ich meine Antwort hier gefunden: stackoverflow.com/questions/82437/…
Mattias Nordqvist

Antworten:

15

Neu in C # 6.0:

Wenn Sie den Setter nur in Ihrem Konstruktor aufrufen, können Sie dieses Problem mithilfe schreibgeschützter Eigenschaften beheben.

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;}
}
Brian
quelle
4
Dies ist zwar wahr, aber keine gute allgemeine Lösung für das Problem. Sicherlich wird es viele Fälle geben, in denen ein öffentlicher Setter gewünscht wird.
Tom Bogle
26

Eine mögliche Antwort wäre, den Getter zu überschreiben und dann eine separate Setter-Methode zu implementieren. Wenn Sie nicht möchten, dass der Eigenschaftssetzer in der Basis definiert wird, haben Sie nicht viele andere Optionen.

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

public void SetMyPop(double value)
{
    _myPop = value;
}
David M.
quelle
17

Es ist nicht möglich zu tun, was Sie wollen. Sie müssen den Setter in der abstrakten Eigenschaft definieren, sonst können Sie ihn nicht richtig überschreiben.

Der einzige Fall, in dem ich weiß, wo ein Getter definiert und ein Getter / Setter implementiert ist, ist die Verwendung einer Schnittstelle:

public interface IBaseInterface
{
    double MyPop { get; }
}

public class DClass : IBaseInterface
{
    public double MyPop { get; set; }
}
Laurent Etiemble
quelle
12
Kein schlechter Ansatz; Aber manchmal müssen Sie nur eine abstrakte Klasse verwenden, keine Schnittstelle.
Graviton
@Graviton: Ich werde feststellen, dass diese Lösung praktikabler wird, wenn C # Standardschnittstellenmethoden einführt .
Brian
8

Wenn Sie BaseClasssich in Ihrer eigenen Codebasis befinden, können Sie Folgendes tun:

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

BEARBEITEN: Sie können dann eine öffentliche Methode in DClass SetMyProp(double myProp)oder dergleichen erstellen . Das Klassendesign für Ihr Domänenmodell sollte klar sein oder für sich selbst sprechen, warum Sie die Eigenschaft nicht direkt in der Basisklasse festlegen können und warum Sie dies in der abgeleiteten Klasse tun können.

herzmeister
quelle
OK, aber was ist mit den Unterklassen, die das nicht einstellen müssen MyPop?
Graviton
Vielleicht können sie dann nur von BaseClass und nicht von DClass stammen?
Herzmeister
Ich denke nicht, dass dies eine gute Idee für meine Apps ist. Die Vererbung sollte der is aBeziehung vorbehalten sein , um eine Sprachbeschränkung nicht zu umgehen. Außerdem verwende ich die Vererbung für andere realistischere Zwecke in der Anwendung hier.
Graviton
1
Ich hatte eigentlich nie Probleme mit diesem Problem und ich bin ein Fan von OO-Korrektheit, glauben Sie mir ;-) ... (obwohl OO Grenzen hat, wohlgemerkt) ... Vielleicht, wenn Sie mir von Ihrem genauen Szenario erzählen, das ich machen könnte ein Vorschlag über Ihr Modell.
Herzmeister
1
Ich habe mehr über ein Beispiel aus der realen Welt nachgedacht, das ich gerne sehen würde, was Sie modellieren möchten. Zum Beispiel Ihre Tier-, Giraffen- und Löwenklassen oder Ihre CelestialBody-, Meteor- und Planetenklassen.
Herzmeister
5

Sind Sie sicher, dass das, was Sie versuchen, ein gutes Design wäre, wenn Sie einen Weg finden würden, dies zu tun?

Es würde Objekten der Unterklasse ermöglichen, Statusänderungen vorzunehmen, die Objekte der übergeordneten Klasse nicht vornehmen können. Würde das nicht gegen das Liskov-Substitutionsprinzip verstoßen?

Mattjames
quelle
2

Sie könnten so etwas tun:

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

Die Verwendung von TestDerived bietet die Funktionalität, die Sie suchen. Der einzige Nachteil, den ich an dieser Methode erkennen kann, ist, dass Sie jede abstrakte Methode in TestDerivedHelper implementieren müssen, aber Sie erhalten später mehr Kontrolle.

Hoffe das hilft. ;)

gotopie
quelle
2

Der Grund, warum dies nicht möglich ist, liegt in der Art und Weise, wie Parameter von C # "magicked" werden. Wenn Sie einen Parameter definieren, erstellt C # einen privaten Feld, das vom impliziten Getter und Setter bearbeitet wird. Wenn die Basisklasse keinen Setter enthält, kann diese Variable nicht von einer in eine Unterklasse geschriebenen Methode geändert werden (da das private Flag sogar Unterklassen den Zugriff darauf untersagt). Normalerweise wird stattdessen der implizite Setter der Basisklasse verwendet.

Ich würde nicht empfehlen, die Menge in die Basisklasse aufzunehmen, wenn dies nicht alle Unterklassen können, da dies gegen das gesamte Prinzip der polymorphen Programmierung verstößt (jede in der abstrakten Klasse definierte abstrakte Methode muss von einer Unterklasse implementiert werden). Das Erstellen einer speziellen Setter-Methode, wie in anderen Antworten beschrieben, ist wahrscheinlich der beste Weg.

Andrew Monteith
quelle
1

Belagerung

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

Die Verwendung von TestDerived bietet die Funktionalität, die Sie suchen. Der einzige Nachteil, den ich an dieser Methode erkennen kann, ist, dass Sie jede abstrakte Methode in TestDerivedHelper implementieren müssen, aber Sie erhalten später mehr Kontrolle.

Ich benutze diesen Ansatz und funktioniert sehr gut für mich. Außerdem habe ich meine "TestDerivedHelper" -Klasse abstrakt gemacht, dann müssen alle Methoden in der "TestDerived" -Klasse implementiert werden.

Thiago Romam
quelle
1

Obwohl dieser Thread alt ist, stelle ich meine Lösung auf, falls es jemandem hilft. Es ist nicht mein eigenes, sondern basiert auf Antworten in anderen SO-Themen.

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

Diese Lösung fügt eine zusätzliche Codezeile für jede solche Eigenschaft hinzu, für die ein Accessor geändert werden muss. Die externe Sichtbarkeit wird jedoch nicht geändert und bietet die erforderlichen Funktionen.

Falcon08
quelle
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
}

Wenn Sie die Eigenschaft von außen festlegen müssen, ist DClasses möglicherweise besser, den Setter in die Basisklasse einzufügen.

Darin Dimitrov
quelle
-2

BEARBEITEN:

OK, ich war vielleicht voreilig mit dieser Antwort, aber ich habe jetzt noch etwas darüber nachgedacht.

Müssen Sie eine abstrakte Basisklasse verwenden? Wenn dies nicht erforderlich ist, versuchen Sie Folgendes:

public interface ISomeRelevantName
{
    double MyPop { get; }
}

public class DClass : ISomeRelevantName
{
    public double MyPop { get; set; }
}
Codesleuth
quelle
2
Wird sich beschweren, dass die abstrakte Eigenschaft MyPopvon BaseClassnicht in der abgeleiteten Klasse implementiert ist.
Joey
Hmm, meine Antwort beschämt mich :( Ich versuche dies mit einer Bearbeitung zu korrigieren.
Codesleuth
-3

Warum nicht einfach eine Eigenschaft in der Basisklasse haben, die einen privaten Setter hat, dann in Ihrer Unterklasse, die den Setter benötigt, diese überschreiben und öffentlich machen.

Pondidum
quelle
6
abstrakter Accessor kann nicht privat sein
Graviton
das Analogon einer abstractEigenschaft getoder eines setAccessors privateist protected. Also in der Basisklasse wollen Sie:public abstract MyProp { get; protected set; }
JoeBrockhaus
-3

Sie können den Set-Accessor nicht überschreiben, da für die Basisklasse kein Set-Accessor definiert ist.

Sie können das newSchlüsselwort verwenden, um die Implementierung der Basisklassen auszublenden. Dies ist jedoch möglicherweise nicht das, was Sie möchten.

Rune Grimstad
quelle
1
Die Basisklasse ist abstrakt und hat keine Implementierung , newkann daher nicht funktionieren.
Joey
Zur Verdeutlichung: Dies funktioniert nicht, da die abgeleitete Klasse die abstrakte Eigenschaft nicht implementiert. Der obige Kommentar war möglicherweise leicht ungenau.
Joey
Mein Kommentar war ebenfalls irreführend (schlug vor, dass "neu" überhaupt funktionieren könnte), also entfernte ich ihn. Danke für das Update.
Benjamin Podszun