Überschreiben von Feldern oder Eigenschaften in Unterklassen

145

Ich habe eine abstrakte Basisklasse und möchte ein Feld oder eine Eigenschaft deklarieren, die in jeder Klasse, die von dieser übergeordneten Klasse erbt, einen anderen Wert hat.

Ich möchte es in der Basisklasse definieren, damit ich es in einer Basisklassenmethode referenzieren kann - zum Beispiel, wenn ToString überschrieben wird, um zu sagen, dass dieses Objekt vom Typ Eigenschaft / Feld ist . Ich habe drei Möglichkeiten, wie ich das sehen kann, aber ich habe mich gefragt - was ist die beste oder akzeptierte Art, dies zu tun? Neuling Frage, sorry.

Option 1:
Verwenden Sie eine abstrakte Eigenschaft und überschreiben Sie sie für die geerbten Klassen. Dies profitiert von der Durchsetzung (Sie müssen es überschreiben) und es ist sauber. Es fühlt sich jedoch etwas falsch an, einen Hardcode-Wert zurückzugeben, anstatt ein Feld zu kapseln, und es sind ein paar Codezeilen statt nur. Ich muss auch einen Körper für "set" deklarieren, aber das ist weniger wichtig (und es gibt wahrscheinlich einen Weg, das zu vermeiden, was mir nicht bekannt ist).

abstract class Father
{
    abstract public int MyInt { get; set;}
}

class Son : Father
{
    public override int MyInt
    {
        get { return 1; }
        set { }
    }
}

Option 2
Ich kann ein öffentliches Feld (oder ein geschütztes Feld) deklarieren und es in der geerbten Klasse explizit überschreiben. Das folgende Beispiel gibt mir eine Warnung, "neu" zu verwenden, und ich kann das wahrscheinlich tun, aber es fühlt sich falsch an und bricht den Polymorphismus, worum es ging. Scheint keine gute Idee zu sein ...

abstract class Mother
{
    public int MyInt = 0;
}

class Daughter : Mother
{
    public int MyInt = 1;
}

Option 3
Ich kann ein geschütztes Feld verwenden und den Wert im Konstruktor festlegen. Dies scheint ziemlich ordentlich zu sein, aber ich muss sicherstellen, dass der Konstruktor dies immer festlegt. Bei mehreren überladenen Konstruktoren besteht immer die Möglichkeit, dass ein Codepfad den Wert nicht festlegt.

abstract class Aunt
{
    protected int MyInt;
}

class Niece : Aunt
{
    public Niece()
    {
        MyInt = 1;
    }
}

Es ist eine theoretische Frage, und ich denke, die Antwort muss Option 1 sein, da dies die einzig sichere Option ist, aber ich habe gerade C # in den Griff bekommen und wollte dies Leuten mit mehr Erfahrung stellen.

Frans
quelle
abstract public int MyInt {get; set;} => öffentliche abstrakte Zeichenfolge IntentName {get; set;}: D
Navid Golforoushan

Antworten:

136

Von den drei Lösungen nur 1 Option ist polymorph .

Felder selbst können nicht überschrieben werden. Genau aus diesem Grund gibt Option 2 die neue Keyword-Warnung zurück.

Die Lösung für die Warnung besteht nicht darin, das Schlüsselwort "new" anzuhängen, sondern Option 1 zu implementieren.

Wenn Ihr Feld polymorph sein soll, müssen Sie es in eine Eigenschaft einschließen.

Option 3 ist in Ordnung, wenn Sie kein polymorphes Verhalten benötigen. Beachten Sie jedoch, dass die abgeleitete Klasse beim Zugriff auf die Eigenschaft MyInt zur Laufzeit keine Kontrolle über den zurückgegebenen Wert hat. Die Basisklasse selbst kann diesen Wert zurückgeben.

So könnte eine wirklich polymorphe Implementierung Ihrer Eigenschaft aussehen, sodass die abgeleiteten Klassen die Kontrolle behalten .

abstract class Parent
{
    abstract public int MyInt { get; }
}

class Father : Parent
{
    public override int MyInt
    {
        get { /* Apply formula "X" and return a value */ }
    }
}

class Mother : Parent
{
    public override int MyInt
    {
        get { /* Apply formula "Y" and return a value */ }
    }
}
Preets
quelle
153
Abgesehen davon denke ich wirklich, dass der Vater die Formel "Y" und die Mutter logischerweise "X" anwenden sollte.
Peter - Reinstate Monica
4
Was wäre, wenn ich eine Standardimplementierung in Parent bereitstellen wollte und diese nicht abstrakt sein soll?
Aaron Franke
@AaronFranke Signatur erstellen: public virtual int MyInt {get; }
Ted Bigham
@ Peter-ReinstateMonica Das wäre "genetische Programmierung"
devinbost
18

Option 2 ist kein Starter - Sie können Felder nicht überschreiben , sondern nur ausblenden .

Persönlich würde ich mich jedes Mal für Option 1 entscheiden. Ich versuche, Felder jederzeit privat zu halten. Das ist natürlich der Fall, wenn Sie wirklich in der Lage sein müssen, die Eigenschaft überhaupt zu überschreiben. Eine andere Option besteht darin, eine schreibgeschützte Eigenschaft in der Basisklasse zu haben, die über einen Konstruktorparameter festgelegt wird:

abstract class Mother
{
    private readonly int myInt;
    public int MyInt { get { return myInt; } }

    protected Mother(int myInt)
    {
        this.myInt = myInt;
    }
}

class Daughter : Mother
{
    public Daughter() : base(1)
    {
    }
}

Dies ist wahrscheinlich der am besten geeignete Ansatz, wenn sich der Wert während der Lebensdauer der Instanz nicht ändert.

Jon Skeet
quelle
Können wir sagen, dass dies jetzt nicht korrekt ist, basierend auf dieser msdn.microsoft.com/en-us/library/9fkccyh4.aspx Der msdn-Artikel zeigt, dass Sie Eigenschaften überschreiben können
Codingbiz
1
@codingbiz: Wo spricht meine Antwort über Eigenschaften? Felder und Eigenschaften sind nicht dasselbe.
Jon Skeet
@codingbiz: (Meine Antwort spricht jetzt zugegebenermaßen über Eigenschaften - aber es wurde nie gesagt, dass Sie sie nicht überschreiben können. Sie sagte - und sagt - dass Sie Felder nicht überschreiben können , was immer noch korrekt ist.)
Jon Skeet
7

Option 2 ist eine schlechte Idee. Es wird zu etwas führen, das als Schatten bezeichnet wird. Grundsätzlich haben Sie zwei verschiedene "MyInt" -Mitglieder, eines bei der Mutter und das andere bei der Tochter. Das Problem dabei ist, dass in der Mutter implementierte Methoden auf das "MyInt" der Mutter verweisen, während in der Tochter implementierte Methoden auf das "MyInt" der Tochter verweisen. Dies kann zu ernsthaften Lesbarkeitsproblemen und später zu Verwirrung führen.

Persönlich denke ich, dass die beste Option 3 ist; weil es einen klaren zentralisierten Wert bietet und von Kindern intern referenziert werden kann, ohne dass sie ihre eigenen Felder definieren müssen - was das Problem bei Option 1 ist.


quelle
6

Du könntest das tun

class x
{
    private int _myInt;
    public virtual int myInt { get { return _myInt; } set { _myInt = value; } }
}

class y : x
{
    private int _myYInt;
    public override int myInt { get { return _myYInt; } set { _myYInt = value; } }
}

Mit virtual können Sie einer Eigenschaft einen Body zuweisen, der etwas tut, und Unterklassen können ihn dennoch überschreiben.

Joshua G.
quelle
4

Sie könnten so etwas definieren:

abstract class Father
{
    //Do you need it public?
    protected readonly int MyInt;
}

class Son : Father
{
    public Son()
    {
        MyInt = 1;
    }
}

Durch Festlegen des Werts als schreibgeschützt wird sichergestellt, dass der Wert für diese Klasse während der Lebensdauer des Objekts unverändert bleibt.

Ich nehme an, die nächste Frage ist: Warum brauchst du sie?

Ameise
quelle
Statisch ist eine schlechte Wortwahl, da dies impliziert, dass der Wert dann von allen Instanzen der Klasse geteilt wird, was natürlich nicht der Fall ist.
Winston Smith
3

Wenn Sie eine Klasse erstellen und möchten, dass es einen Basiswert für die Eigenschaft gibt, verwenden Sie das virtualSchlüsselwort in der Basisklasse. Auf diese Weise können Sie die Eigenschaft optional überschreiben.

Verwenden Sie Ihr Beispiel oben:

//you may want to also use interfaces.
interface IFather
{
    int MyInt { get; set; }
}


public class Father : IFather
{
    //defaulting the value of this property to 1
    private int myInt = 1;

    public virtual int MyInt
    {
        get { return myInt; }
        set { myInt = value; }
    }
}

public class Son : Father
{
    public override int MyInt
    {
        get {

            //demonstrating that you can access base.properties
            //this will return 1 from the base class
            int baseInt = base.MyInt;

            //add 1 and return new value
            return baseInt + 1;
        }
        set
        {
            //sets the value of the property
            base.MyInt = value;
        }
    }
}

In einem Programm:

Son son = new Son();
//son.MyInt will equal 2
Keith Aymar
quelle
0

Ich würde mich für Option 3 entscheiden, habe aber eine abstrakte setMyInt-Methode, die von Unterklassen implementiert werden muss. Auf diese Weise haben Sie nicht das Problem, dass eine abgeleitete Klasse vergisst, sie im Konstruktor festzulegen.

abstract class Base 
{
 protected int myInt;
 protected abstract void setMyInt();
}

class Derived : Base 
{
 override protected void setMyInt()
 {
   myInt = 3;
 }
}

Übrigens mit Option eins, wenn Sie set nicht angeben; In Ihrer abstrakten Basisklasseneigenschaft muss die abgeleitete Klasse sie nicht implementieren.

abstract class Father
{
    abstract public int MyInt { get; }
}

class Son : Father
{
    public override int MyInt
    {
        get { return 1; }
    }
}
Winston Smith
quelle
0

Sie können mit Option 3 fortfahren. Wenn Sie Ihre abstrakte Basisklasse so ändern, dass der Eigenschaftswert im Konstruktor erforderlich ist, werden Sie keine Pfade verpassen. Ich würde diese Option wirklich in Betracht ziehen.

abstract class Aunt
{
    protected int MyInt;
    protected Aunt(int myInt)
    {
        MyInt = myInt;
    }

}

Natürlich haben Sie dann immer noch die Möglichkeit, das Feld privat zu machen und dann je nach Bedarf einen geschützten oder öffentlichen Güter zu entlarven.

Blair Conrad
quelle
0

Ich war das...

namespace Core.Text.Menus
{
    public abstract class AbstractBaseClass
    {
        public string SELECT_MODEL;
        public string BROWSE_RECORDS;
        public string SETUP;
    }
}

namespace Core.Text.Menus
{
    public class English : AbstractBaseClass
    {
        public English()
        {
            base.SELECT_MODEL = "Select Model";
            base.BROWSE_RECORDS = "Browse Measurements";
            base.SETUP = "Setup Instrument";
        }
    }
}

Auf diese Weise können Sie weiterhin Felder verwenden.

8r13n
quelle
Ich denke, dies ist eine gute Ad-hoc-Lösung für das Prototyping oder Demo.
Zimano
0

Die Beispielimplementierung, wenn Sie eine abstrakte Klasse mit Implementierung haben möchten. Unterklassen müssen:

  1. Parametrieren Sie die Implementierung einer abstrakten Klasse.
  2. Erben Sie die Implementierung der abstrakten Klasse vollständig.
  3. Haben Sie Ihre eigene Implementierung.

In diesem Fall sollten die für die Implementierung erforderlichen Eigenschaften nur für die abstrakte Klasse und ihre eigene Unterklasse zur Verfügung stehen.

    internal abstract class AbstractClass
    {
        //Properties for parameterization from concrete class
        protected abstract string Param1 { get; }
        protected abstract string Param2 { get; }

        //Internal fields need for manage state of object
        private string var1;
        private string var2;

        internal AbstractClass(string _var1, string _var2)
        {
            this.var1 = _var1;
            this.var2 = _var2;
        }

        internal void CalcResult()
        {
            //The result calculation uses Param1, Param2, var1, var2;
        }
    }

    internal class ConcreteClassFirst : AbstractClass
    {
        private string param1;
        private string param2;
        protected override string Param1 { get { return param1; } }
        protected override string Param2 { get { return param2; } }

        public ConcreteClassFirst(string _var1, string _var2) : base(_var1, _var2) { }

        internal void CalcParams()
        {
            //The calculation param1 and param2
        }
    }

    internal class ConcreteClassSecond : AbstractClass
    {
        private string param1;
        private string param2;

        protected override string Param1 { get { return param1; } }

        protected override string Param2 { get { return param2; } }

        public ConcreteClassSecond(string _var1, string _var2) : base(_var1, _var2) { }

        internal void CalcParams()
        {
            //The calculation param1 and param2
        }
    }

    static void Main(string[] args)
    {
        string var1_1 = "val1_1";
        string var1_2 = "val1_2";

        ConcreteClassFirst concreteClassFirst = new ConcreteClassFirst(var1_1, var1_2);
        concreteClassFirst.CalcParams();
        concreteClassFirst.CalcResult();

        string var2_1 = "val2_1";
        string var2_2 = "val2_2";

        ConcreteClassSecond concreteClassSecond = new ConcreteClassSecond(var2_1, var2_2);
        concreteClassSecond.CalcParams();
        concreteClassSecond.CalcResult();

        //Param1 and Param2 are not visible in main method
    }
Belomestnykh Sergey
quelle