Abstrak Properti Di Kelas Basis Untuk Memaksa Programmer Untuk Mendefinisikannya

10

Saya mengkode dengan pola keadaan untuk perangkat tertanam. Saya memiliki kelas dasar / abstrak yang disebut State dan kemudian setiap kelas state diskrit (konkret) mengimplementasikan Kelas State abstrak.

Di Kelas Negara saya memiliki beberapa Metode Abstrak. Jika saya tidak menerapkan metode abstrak di kelas diskrit (beton), Visual Studio akan memberikan kesalahan seperti ini:

... Kesalahan 1 'myConcreteState' tidak menerapkan anggota abstrak bawaan 'myAbstractState'

Sekarang: Saya mencoba membuat properti String untuk setiap State yang disebut StateName. Setiap kali saya membuat kelas konkret baru, saya perlu mendefinisikan StateName. Saya ingin VS melempar kesalahan jika saya tidak menggunakannya. Apakah ada cara sederhana untuk melakukan hal ini?

Saya sudah mencoba ini di kelas abstrak / dasar:

public abstract string StateName { get; set; }

Tetapi saya tidak perlu menerapkan metode Get and Set di setiap Negara.

Pertanyaan yang Direvisi: Dalam situasi yang ideal, setiap Kelas Negara akan diharuskan untuk memiliki StateName yang didefinisikan dan diwarisi dari kelas dasar yang abstrak.

StateName = "MyState1"; //or whatever the state's name is

Jika pernyataan itu hilang maka Visual Studio akan menghasilkan kesalahan seperti yang dijelaskan di atas. Apakah ini mungkin dan jika iya, bagaimana?

GisMofx
sumber
2
Saya tidak mengerti pertanyaannya.
Ben Aaronson
Saya kira cara "benar" untuk melakukan ini adalah memiliki konstruktor yang dilindungi pada kelas dasar yang memerlukan nama negara sebagai parameter.
Roman Reiner
@RomanReiner Saya berpikir untuk melakukan itu juga..tetapi tampaknya berlebihan karena setiap kali mengubah / memanggil negara, saya harus mengetikkan namanya.
GisMofx
@ BenAaronson Saya sudah mengklarifikasi pertanyaan saya di dekat bagian bawah.
GisMofx
Apakah nama negara konstan per jenis atau konstanta per instance?
Roman Reiner

Jawaban:

16

Saya kira cara "benar" untuk melakukan ini adalah memiliki konstruktor yang dilindungi pada kelas dasar yang memerlukan nama negara sebagai parameter.

public abstract class State
{
    private readonly string _name;

    protected State(string name)
    {
        if(String.IsNullOrEmpty(name))
            throw new ArgumentException("Must not be empty", "name");

        _name = name;
    }

    public string Name { get { return _name; } }
}

Negara konkret kemudian menyediakan konstruktor publik yang secara implisit memanggil konstruktor kelas dasar dengan nama yang sesuai.

public abstract class SomeState : State
{
    public SomeState() : base("The name of this state")
    {
        // ...
    }
}

Karena kelas dasar tidak mengekspos konstruktor lain (baik yang dilindungi maupun publik), masing-masing kelas yang diwariskan harus melalui konstruktor tunggal ini dan karenanya perlu mendefinisikan nama.

Perhatikan bahwa Anda tidak perlu memberikan nama ketika Anda membuat keadaan konkret karena konstruktornya yang menangani itu:

var someState = new SomeState(); // No need to define the name here
var name = someState.Name; // returns "The name of this state"
Roman Reiner
sumber
1
Terima kasih! Pada kenyataannya, konstruktor kelas dasar saya sekarang mengambil argumen tambahan; jadi saya punya dua input. Segera setelah saya mengatur ini, saya mendapatkan kesalahan yang saya butuhkan argumen tambahan di konstruktor kelas negara ...:base(arg1, arg2)! Ini adalah solusi yang saya cari. Ini benar-benar membantu menjaga kode negara saya lebih konsisten.
GisMofx
3

Pada C # 6 (saya percaya - C #: Baru dan Peningkatan C # 6.0 ) Anda dapat membuat properti pengambil saja. Jadi Anda bisa mendeklarasikan kelas dasar Anda seperti itu -

public abstract class State
{
    public abstract string name { get; }

    // Your other functions....
}

Dan kemudian di sub-kelas Anda, Anda dapat menerapkan Stateseperti -

public class SomeState : State
{
    public override string name { get { return "State_1"; } }
}

Atau bahkan lebih rapi menggunakan operator lambda -

public class SomeState : State
{
    public override string name => "State_1";
}

Keduanya akan selalu mengembalikan "State_1" dan tidak berubah.

John C
sumber
1
ini dapat ditulis public override string name { get; } = "State_1";yang kemudian tidak perlu mengevaluasi kembali nilainya setiap kali.
Dave Cousineau
2

Persyaratan Anda merupakan kontradiksi:

Saya mencoba membuat properti String untuk setiap State yang disebut StateName.

vs.

Tapi saya tidak perlu menerapkan semua itu di setiap Negara.

Tidak ada fitur bahasa yang memungkinkan Anda untuk memaksa keberadaan anggota hanya dalam beberapa sub kelas. Lagi pula, tujuan menggunakan kelas super adalah mengandalkan fakta bahwa semua subclass akan memiliki semua anggota kelas super (dan mungkin lebih). Jika Anda ingin membuat kelas yang bertindak sebagai Statetetapi tidak memiliki nama, maka menurut definisi, mereka seharusnya (/ tidak bisa) tidak menjadi subkelas dari State.

Anda harus mengubah persyaratan Anda atau menggunakan sesuatu selain warisan sederhana.

Solusi yang mungkin dengan kode seperti itu adalah membuat metode secara Stateabstrak dan mengembalikan string kosong.

batal
sumber
Saya pikir Anda mengambil pernyataan kedua di luar konteks, tetapi saya akan mengklarifikasi. Saya tidak memerlukan metode Get / Set, saya hanya ingin StateName = "MyState1"di setiap Negara. Jika saya tidak memiliki pernyataan di kelas negara, maka idealnya Visual Studio akan menghasilkan kesalahan.
GisMofx
3
@GisMofx Jika Anda ingin memaksakan nama negara di setiap subclass, buatlah abstrak tetapi tidak memerlukan status. Kemudian setiap subclass harus menyediakan return "MyState1"sebagai implementasinya, dan dapat menambahkan penyimpanan yang dapat berubah untuk nilai jika mereka membutuhkannya. Tetapi Anda perlu mengklarifikasi persyaratan dalam pertanyaan - apakah setiap jenis negara memerlukan StateName yang dapat dibaca, dan apakah semua jenis negara mengharuskannya untuk dimutasi setelah penciptaan?
Pete Kirkham
@PeteKirkham setiap negara bagian memerlukan nama, itu tidak boleh dimutasi setelah dibuat. Nama negara adalah statis / tidak dapat diubah.
GisMofx