Mengganti bidang atau properti dalam subclass

145

Saya memiliki kelas dasar abstrak dan saya ingin mendeklarasikan bidang atau properti yang akan memiliki nilai berbeda di setiap kelas yang diwarisi dari kelas induk ini.

Saya ingin mendefinisikannya dalam baseclass sehingga saya dapat merujuknya dalam metode kelas dasar - misalnya menimpa ToString untuk mengatakan "Objek ini adalah tipe properti / bidang ". Saya punya tiga cara yang bisa saya lihat untuk melakukan ini, tetapi saya bertanya-tanya - apa cara terbaik atau diterima untuk melakukan ini? Pertanyaan pemula, maaf.

Opsi 1:
Gunakan Properti abstrak dan menimpanya di kelas yang diwarisi. Ini manfaat dari ditegakkan (Anda harus menimpanya) dan bersih. Tapi, rasanya sedikit salah untuk mengembalikan nilai kode keras daripada merangkum bidang dan itu adalah beberapa baris kode, bukan hanya. Saya juga harus mendeklarasikan badan untuk "mengatur" tetapi itu kurang penting (dan mungkin ada cara untuk menghindari apa yang saya tidak sadari).

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

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

Opsi 2
Saya bisa mendeklarasikan bidang publik (atau bidang yang dilindungi) dan secara eksplisit menimpanya di kelas yang diwarisi. Contoh di bawah ini akan memberi saya peringatan untuk menggunakan "baru" dan saya mungkin bisa melakukan itu, tetapi rasanya salah dan itu merusak polimorfisme, yang merupakan intinya. Sepertinya bukan ide yang bagus ...

abstract class Mother
{
    public int MyInt = 0;
}

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

Opsi 3
Saya bisa menggunakan bidang yang dilindungi dan menetapkan nilai dalam konstruktor. Ini tampaknya cukup rapi tetapi bergantung pada saya untuk memastikan konstruktor selalu menetapkan ini dan dengan beberapa konstruktor kelebihan beban selalu ada kesempatan beberapa jalur kode tidak akan menetapkan nilai.

abstract class Aunt
{
    protected int MyInt;
}

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

Ini sedikit pertanyaan teoretis dan saya kira jawabannya harus menjadi opsi 1 karena ini adalah satu-satunya pilihan yang aman, tetapi saya hanya ingin memahami C # dan ingin menanyakan hal ini kepada orang-orang dengan pengalaman lebih.

Frans
sumber
publik abstrak int MyInt {get; set;} => string abstrak publik IntentName {get; set;}: D
Navid Golforoushan

Jawaban:

136

Dari tiga solusi, hanya Opsi 1 yang polimorfik .

Fields sendiri tidak bisa diganti. Itulah sebabnya Opsi 2 mengembalikan peringatan kata kunci baru .

Solusi untuk peringatan ini bukan untuk menambahkan kata kunci "baru", tetapi untuk mengimplementasikan Opsi 1.

Jika Anda perlu bidang Anda menjadi polimorfik, Anda harus membungkusnya dalam Properti.

Opsi 3 OK jika Anda tidak memerlukan perilaku polimorfik. Anda harus ingat, bahwa ketika runtime properti MyInt diakses, kelas turunan tidak memiliki kontrol pada nilai yang dikembalikan. Kelas dasar dengan sendirinya mampu mengembalikan nilai ini.

Ini adalah bagaimana implementasi yang benar-benar polimorfik dari properti Anda terlihat, memungkinkan kelas turunan untuk memegang kendali .

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 */ }
    }
}
Preet
sumber
154
Sebagai tambahan, saya benar-benar berpikir Bapa harus menerapkan rumus "Y", dan Ibu, secara logis, "X".
Peter - Reinstate Monica
4
Bagaimana jika saya ingin menyediakan implementasi default di Induk dan memilikinya tidak abstrak?
Aaron Franke
@AaronFranke Buat tanda tangan: int virtual publik MyInt {get; }
Ted Bigham
@ Peter-ReinstateMonica Itu akan menjadi "pemrograman genetik"
devinbost
18

Opsi 2 adalah non-starter - Anda tidak bisa menimpa bidang, Anda hanya bisa menyembunyikannya .

Secara pribadi, saya akan memilih opsi 1 setiap kali. Saya mencoba untuk menjaga bidang pribadi setiap saat. Itu jika Anda benar-benar harus dapat menimpa properti sama sekali, tentu saja. Opsi lain adalah memiliki properti read-only di kelas dasar yang ditetapkan dari parameter konstruktor:

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

Itu mungkin pendekatan yang paling tepat jika nilainya tidak berubah selama masa contoh.

Jon Skeet
sumber
Bisakah kita mengatakan ini sekarang tidak benar berdasarkan ini msdn.microsoft.com/en-us/library/9fkccyh4.aspx Artikel msdn menunjukkan Anda dapat menimpa properti
codingbiz
1
@codingbiz: di mana jawaban saya berbicara tentang properti? Bidang dan properti bukan hal yang sama.
Jon Skeet
@codingbiz: (Jawaban saya sekarang berbicara tentang properti, diakui - tetapi tidak pernah mengatakan Anda tidak bisa menimpa mereka. Dikatakan - dan mengatakan - bahwa Anda tidak dapat menimpa bidang , yang masih benar.)
Jon Skeet
7

opsi 2 adalah ide yang buruk. Ini akan menghasilkan sesuatu yang disebut membayangi; Pada dasarnya Anda memiliki dua anggota "MyInt" yang berbeda, satu di ibu, dan yang lainnya di putri. Masalah dengan ini, adalah bahwa metode yang diterapkan pada ibu akan referensi ibu "MyInt" sementara metode yang diterapkan pada anak perempuan akan referensi anak perempuan "MyInt". ini dapat menyebabkan beberapa masalah keterbacaan serius, dan kebingungan di kemudian hari.

Secara pribadi, saya pikir pilihan terbaik adalah 3; karena memberikan nilai terpusat yang jelas, dan dapat dirujuk secara internal oleh anak-anak tanpa perlu mendefinisikan bidang mereka sendiri - yang merupakan masalah dengan opsi 1.


sumber
6

Anda bisa melakukan ini

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

virtual memungkinkan Anda mendapatkan properti benda yang melakukan sesuatu dan masih membiarkan sub-kelas menimpanya.

Joshua G
sumber
4

Anda dapat mendefinisikan sesuatu seperti ini:

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

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

Dengan menetapkan nilai sebagai hanya baca, itu memastikan bahwa nilai untuk kelas itu tetap tidak berubah selama masa objek.

Saya kira pertanyaan selanjutnya adalah: mengapa Anda membutuhkannya?

Semut
sumber
Statis adalah pilihan kata yang buruk karena mengandung arti bahwa nilai tersebut kemudian dibagi di antara semua instance kelas, yang tentu saja bukan.
Winston Smith
3

Jika Anda sedang membangun kelas dan Anda ingin ada nilai dasar untuk properti, maka gunakan virtualkata kunci di kelas dasar. Ini memungkinkan Anda untuk mengganti properti secara opsional.

Menggunakan contoh Anda di atas:

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

Dalam sebuah program:

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

Saya akan menggunakan opsi 3, tetapi memiliki metode setMyInt abstrak yang dipaksa untuk diimplementasikan oleh subclass. Dengan cara ini Anda tidak akan memiliki masalah kelas turunan lupa untuk mengaturnya di konstruktor.

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

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

By the way, dengan opsi satu, jika Anda tidak menentukan set; di properti kelas dasar abstrak Anda, kelas turunan tidak perlu mengimplementasikannya.

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

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

Anda bisa pergi dengan opsi 3 jika Anda memodifikasi kelas dasar abstrak Anda untuk memerlukan nilai properti di konstruktor, Anda tidak akan kehilangan jalur apa pun. Saya benar-benar mempertimbangkan opsi ini.

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

}

Tentu saja, Anda kemudian masih memiliki opsi untuk menjadikan bidang tersebut pribadi dan kemudian, tergantung pada kebutuhan, mengungkap pembuat properti yang dilindungi atau properti publik.

Blair Conrad
sumber
0

Saya melakukan ini ...

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

Dengan cara ini Anda masih bisa menggunakan bidang.

8r13n
sumber
Saya merasa ini bagus sebagai solusi ad-hoc untuk pembuatan prototipe atau demo.
Zimano
0

Contoh implementasi ketika Anda ingin memiliki kelas abstrak dengan implementasi. Subkelas harus:

  1. Parameterkan implementasi kelas abstrak.
  2. Sepenuhnya mewarisi implementasi kelas abstrak;
  3. Miliki implementasi Anda sendiri.

Dalam hal ini, properti yang diperlukan untuk implementasi seharusnya tidak tersedia untuk digunakan kecuali untuk kelas abstrak dan subkelasnya sendiri.

    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
sumber