TL; DR Gulir ke bagian paling bawah.
Dari apa yang saya lihat, Anda menerapkan bahasa baru di atas C #. Enum tampaknya menunjukkan tipe pengidentifikasi (atau, apa pun yang memiliki nama dan yang muncul dalam kode sumber bahasa baru), yang tampaknya diterapkan pada node yang akan ditambahkan ke representasi pohon dari program.
Dalam situasi khusus ini, ada sangat sedikit perilaku polimorfik antara berbagai jenis node. Dengan kata lain, sementara itu perlu bagi pohon untuk dapat mengandung node dari jenis yang sangat berbeda (varian), visitasi sebenarnya node ini akan pada dasarnya resor untuk raksasa if-then-else rantai (atau instanceof
/ is
cek). Cek raksasa ini kemungkinan akan terjadi di banyak tempat berbeda di seluruh proyek. Ini adalah alasan mengapa enum mungkin tampak membantu, atau, mereka setidaknya sama bermanfaatnya dengan instanceof
/ is
memeriksa.
Pola pengunjung mungkin masih bermanfaat. Dengan kata lain, ada berbagai gaya pengkodean yang dapat digunakan sebagai ganti rantai raksasa instanceof
. Namun, jika Anda ingin diskusi tentang berbagai manfaat dan kelemahan, Anda akan memilih untuk menampilkan contoh kode dari rantai paling jelek instanceof
dalam proyek, daripada berdalih tentang enum.
Ini bukan untuk mengatakan kelas dan hierarki warisan tidak berguna. Justru sebaliknya. Meskipun tidak ada perilaku polimorfik yang bekerja di setiap jenis deklarasi (selain dari fakta bahwa setiap deklarasi harus memiliki Name
properti), ada banyak perilaku polimorfik kaya yang dimiliki oleh saudara kandung terdekat. Misalnya, Function
dan Procedure
mungkin berbagi beberapa perilaku (keduanya bisa dipanggil dan menerima daftar argumen input yang diketik), dan PropertyGet
pasti akan mewarisi perilaku dari Function
(keduanya memiliki a ReturnType
). Anda mungkin menggunakan enum atau pemeriksaan warisan untuk rantai raksasa jika-kemudian-yang lain, tetapi perilaku polimorfik, betapapun terfragmentasi, masih harus diimplementasikan di kelas.
Ada banyak saran online yang melarang penggunaan instanceof
/ is
pemeriksaan berlebihan . Performa bukan salah satu alasannya. Sebaliknya, alasannya adalah untuk mencegah programmer dari organik menemukan perilaku polimorfik cocok, seakan instanceof
/ is
adalah penopang. Tetapi dalam situasi Anda, Anda tidak punya pilihan lain, karena simpul-simpul ini memiliki sedikit persamaan.
Sekarang, inilah beberapa saran konkret.
Ada beberapa cara untuk mewakili pengelompokan non-daun.
Bandingkan kutipan kode asli Anda berikut ...
[Flags]
public enum DeclarationType
{
Member = 1 << 7,
Procedure = 1 << 8 | Member,
Function = 1 << 9 | Member,
Property = 1 << 10 | Member,
PropertyGet = 1 << 11 | Property | Function,
PropertyLet = 1 << 12 | Property | Procedure,
PropertySet = 1 << 13 | Property | Procedure,
LibraryFunction = 1 << 23 | Function,
LibraryProcedure = 1 << 24 | Procedure,
}
ke versi modifikasi ini:
[Flags]
public enum DeclarationType
{
Nothing = 0, // to facilitate bit testing
// Let's assume Member is not a concrete thing,
// which means it doesn't need its own bit
/* Member = 1 << 7, */
// Procedure and Function are concrete things; meanwhile
// they can still have sub-types.
Procedure = 1 << 8,
Function = 1 << 9,
Property = 1 << 10,
PropertyGet = 1 << 11,
PropertyLet = 1 << 12,
PropertySet = 1 << 13,
LibraryFunction = 1 << 23,
LibraryProcedure = 1 << 24,
// new
Procedures = Procedure | PropertyLet | PropertySet | LibraryProcedure,
Functions = Function | PropertyGet | LibraryFunction,
Properties = PropertyGet | PropertyLet | PropertySet,
Members = Procedures | Functions | Properties,
LibraryMembers = LibraryFunction | LibraryProcedure
}
Versi modifikasi ini menghindari mengalokasikan bit ke jenis deklarasi non-konkret. Sebaliknya, tipe deklarasi non-konkret (pengelompokan abstrak tipe deklarasi) hanya memiliki nilai enum yang merupakan bitwise-atau (penyatuan bit) di semua anak-anaknya.
Ada peringatan: jika ada jenis deklarasi abstrak yang memiliki anak tunggal, dan jika ada kebutuhan untuk membedakan antara yang abstrak (orang tua) dari yang konkret (anak), maka yang abstrak masih akan membutuhkan bitnya sendiri. .
Satu peringatan yang khusus untuk pertanyaan ini: a Property
pada awalnya adalah pengidentifikasi (ketika Anda baru saja melihat namanya, tanpa melihat bagaimana itu digunakan dalam kode), tetapi dapat mentransmutasikan ke PropertyGet
/ PropertyLet
/ PropertySet
segera setelah Anda melihat bagaimana itu digunakan dalam kode. Dengan kata lain, pada tahapan parsing yang berbeda, Anda mungkin perlu menandai Property
pengidentifikasi sebagai "nama ini merujuk ke properti", dan kemudian mengubahnya menjadi "baris kode ini mengakses properti ini dengan cara tertentu".
Untuk mengatasi peringatan ini, Anda mungkin perlu dua set enum; satu enum menunjukkan apa nama (pengidentifikasi) itu; enum lain menunjukkan apa yang coba dilakukan oleh kode (misalnya mendeklarasikan isi dari sesuatu; mencoba menggunakan sesuatu dengan cara tertentu).
Pertimbangkan apakah informasi tambahan tentang setiap nilai enum dapat dibaca dari sebuah array sebagai gantinya.
Saran ini saling eksklusif dengan saran lain karena memerlukan konversi nilai kekuatan-dua kembali ke nilai integer kecil non-negatif.
public enum DeclarationType
{
Procedure = 8,
Function = 9,
Property = 10,
PropertyGet = 11,
PropertyLet = 12,
PropertySet = 13,
LibraryFunction = 23,
LibraryProcedure = 24,
}
static readonly bool[] DeclarationTypeIsMember = new bool[32]
{
?, ?, ?, ?, ?, ?, ?, ?, // bit[0] ... bit[7]
true, true, true, true, true, true, ?, ?, // bit[8] ... bit[15]
?, ?, ?, ?, ?, ?, ?, true, // bit[16] ... bit[23]
true, ... // bit[24] ...
}
static bool IsMember(DeclarationType dt)
{
int intValue = (int)dt;
return (intValue < 0 || intValue >= 32) ? false : DeclarationTypeIsMember[intValue];
// you can also throw an exception if the enum is outside range.
}
// likewise for IsFunction(dt), IsProcedure(dt), IsProperty(dt), ...
Maintainability akan menjadi masalah.
Periksa apakah pemetaan satu-ke-satu antara tipe C # (kelas dalam hierarki warisan) dan nilai enum Anda.
(Atau, Anda dapat mengubah nilai enum Anda untuk memastikan pemetaan satu-ke-satu dengan tipe-tipe.)
Di C #, banyak perpustakaan menyalahgunakan Type object.GetType()
metode bagus , baik atau buruk.
Di mana pun Anda menyimpan enum sebagai nilai, Anda mungkin bertanya pada diri sendiri apakah Anda dapat menyimpan enum sebagai Type
nilai.
Untuk menggunakan trik ini, Anda dapat menginisialisasi dua tabel hash read-only, yaitu:
// For disambiguation, I'll assume that the actual
// (behavior-implementing) classes are under the
// "Lang" namespace.
static readonly Dictionary<Type, DeclarationType> TypeToDeclEnum = ...
{
{ typeof(Lang.Procedure), DeclarationType.Procedure },
{ typeof(Lang.Function), DeclarationType.Function },
{ typeof(Lang.Property), DeclarationType.Property },
...
};
static readonly Dictionary<DeclarationType, Type> DeclEnumToType = ...
{
// same as the first dictionary;
// just swap the key and the value
...
};
Pembenaran akhir bagi mereka yang menyarankan kelas dan hierarki warisan ...
Setelah Anda dapat melihat bahwa enum adalah perkiraan untuk hierarki warisan , saran berikut berlaku:
- Desain (atau tingkatkan) hierarki warisan Anda terlebih dahulu,
- Kemudian kembali dan rancang enum Anda untuk memperkirakan hierarki pewarisan itu.
DeclarationType
. Jika saya ingin menentukan apakahx
ini subtipe atau tidaky
, saya mungkin ingin menuliskannya sebagaix.IsSubtypeOf(y)
, bukan sebagaix && y == y
.