Pemahaman saya adalah bahwa [Flag]
enum biasanya digunakan untuk hal-hal yang dapat digabungkan, di mana nilai-nilai individual tidak saling eksklusif .
Sebagai contoh:
[Flags]
public enum SomeAttributes
{
Foo = 1 << 0,
Bar = 1 << 1,
Baz = 1 << 2,
}
Di mana SomeAttributes
nilai apa pun bisa merupakan kombinasi dari Foo
, Bar
dan Baz
.
Dalam skenario kehidupan nyata yang lebih rumit , saya menggunakan enum untuk menggambarkan DeclarationType
:
[Flags]
public enum DeclarationType
{
Project = 1 << 0,
Module = 1 << 1,
ProceduralModule = 1 << 2 | Module,
ClassModule = 1 << 3 | Module,
UserForm = 1 << 4 | ClassModule,
Document = 1 << 5 | ClassModule,
ModuleOption = 1 << 6,
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,
Parameter = 1 << 14,
Variable = 1 << 15,
Control = 1 << 16 | Variable,
Constant = 1 << 17,
Enumeration = 1 << 18,
EnumerationMember = 1 << 19,
Event = 1 << 20,
UserDefinedType = 1 << 21,
UserDefinedTypeMember = 1 << 22,
LibraryFunction = 1 << 23 | Function,
LibraryProcedure = 1 << 24 | Procedure,
LineLabel = 1 << 25,
UnresolvedMember = 1 << 26,
BracketedExpression = 1 << 27,
ComAlias = 1 << 28
}
Jelas suatu pemberian Declaration
tidak dapat berupa a Variable
dan a LibraryProcedure
- dua nilai individual tidak dapat digabungkan .. dan tidak.
Walaupun flag ini sangat berguna (sangat mudah untuk memverifikasi apakah yang diberikan DeclarationType
adalah a Property
atau a Module
), rasanya "salah" karena flag tersebut tidak benar - benar digunakan untuk menggabungkan nilai, tetapi lebih untuk mengelompokkannya menjadi "sub-tipe".
Jadi saya diberitahu bahwa ini adalah penyalahgunaan enum flags - jawaban ini pada dasarnya mengatakan bahwa jika saya memiliki satu set nilai yang berlaku untuk apel dan set lain yang berlaku untuk jeruk, maka saya memerlukan jenis enum berbeda untuk apel dan satu lagi untuk jeruk - yang masalah dengan itu di sini adalah bahwa saya perlu semua deklarasi untuk memiliki antarmuka yang sama, dengan DeclarationType
diekspos di Declaration
kelas dasar : memiliki PropertyType
enum tidak akan berguna sama sekali.
Apakah ini desain yang ceroboh / mengejutkan / kasar? Jika demikian, lalu bagaimana masalah itu biasanya diselesaikan?
sumber
DeclarationType
. Jika saya ingin menentukan apakahx
ini subtipe atau tidaky
, saya mungkin ingin menuliskannya sebagaix.IsSubtypeOf(y)
, bukan sebagaix && y == y
.x.HasFlag(DeclarationType.Member)
dilakukan ....HasFlag
bukanIsSubtypeOf
, maka saya perlu cara lain untuk mengetahui bahwa apa yang sebenarnya dimaksud adalah "adalah subtipe dari". Anda dapat membuat metode ekstensi, tetapi sebagai pengguna, apa yang saya temukan paling mengejutkan adalahDeclarationType
hanya menjadi struct yang dimilikiIsSubtypeOf
sebagai metode nyata .Jawaban:
Ini jelas menyalahgunakan enum dan bendera! Ini mungkin bekerja untuk Anda, tetapi siapa pun yang membaca kode akan sangat bingung.
Jika saya mengerti dengan benar, Anda memiliki klasifikasi deklarasi hirarkis . Ini adalah jauh untuk banyak informasi untuk encode dalam enum tunggal. Tetapi ada alternatif yang jelas: Gunakan kelas dan warisan! Jadi
Member
mewarisi dariDeclarationType
,Property
mewarisi dariMember
dan seterusnya.Enum sesuai dalam beberapa keadaan tertentu: Jika suatu nilai selalu merupakan salah satu dari sejumlah opsi yang terbatas, atau jika itu merupakan kombinasi dari sejumlah opsi (bendera) tertentu. Setiap informasi yang lebih kompleks atau terstruktur dari ini harus direpresentasikan menggunakan objek.
Sunting : Dalam "skenario kehidupan nyata" Anda, tampaknya ada beberapa tempat di mana perilaku dipilih tergantung pada nilai enum. Ini benar-benar antipattern, karena Anda menggunakan
switch
+enum
sebagai "polimorfisme orang miskin". Ubah saja nilai enum menjadi kelas yang berbeda yang merangkum perilaku khusus deklarasi, dan kode Anda akan jauh lebih bersihsumber
ParserRuleContext
kelas yang dihasilkan daripada dengan enum. Kode itu sedang mencoba untuk mendapatkan posisi token tempat memasukkanAs {Type}
klausa dalam deklarasi; iniParserRuleContext
kelas -derived dihasilkan oleh Antr4 per tata bahasa yang mendefinisikan aturan parser - Saya tidak benar-benar mengontrol node pohon parsing [bukan dangkal] warisan hirarki, meskipun saya dapat memanfaatkan merekapartial
-ness untuk membuat mereka mengimplementasikan antarmuka, misalnya untuk membuat mereka memperlihatkan beberapaAsTypeClauseTokenPosition
properti .. banyak pekerjaan.Saya menemukan pendekatan ini cukup mudah untuk dibaca dan dipahami. IMHO, itu bukan sesuatu yang membingungkan. Yang sedang berkata, saya memiliki beberapa kekhawatiran tentang pendekatan ini:
Reservasi utama saya adalah bahwa tidak ada cara untuk menegakkan ini:
Meskipun Anda tidak mendeklarasikan kombinasi di atas, kode ini
Sangat valid. Dan tidak ada cara untuk menangkap kesalahan itu pada waktu kompilasi.
Ada batasan berapa banyak informasi yang dapat Anda encode dalam flag bit, yang mana, 64 bit? Untuk sekarang Anda semakin mendekati ukuran
int
dan jika enum ini terus tumbuh Anda mungkin akhirnya hanya kehabisan bit ...Intinya adalah, saya pikir ini adalah pendekatan yang valid, tetapi saya akan ragu untuk menggunakannya untuk hierarki bendera yang besar / kompleks.
sumber
int
kapasitas adalah masalah nyata.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 denganinstanceof
/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 jelekinstanceof
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
danProcedure
mungkin berbagi beberapa perilaku (keduanya bisa dipanggil dan menerima daftar argumen input yang diketik), danPropertyGet
pasti akan mewarisi perilaku dariFunction
(keduanya memiliki aReturnType
). 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, seakaninstanceof
/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 ...
ke versi modifikasi ini:
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 kePropertyGet
/PropertyLet
/PropertySet
segera setelah Anda melihat bagaimana itu digunakan dalam kode. Dengan kata lain, pada tahapan parsing yang berbeda, Anda mungkin perlu menandaiProperty
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.
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:
Pembenaran akhir bagi mereka yang menyarankan kelas dan hierarki warisan ...
Setelah Anda dapat melihat bahwa enum adalah perkiraan untuk hierarki warisan , saran berikut berlaku:
sumber
Saya menemukan penggunaan bendera Anda sangat cerdas, kreatif, elegan dan berpotensi paling efisien. Saya tidak kesulitan sama sekali membacanya juga.
Bendera adalah sarana pensinyalan, kualifikasi. Jika saya ingin tahu apakah sesuatu itu buah, saya temukan
thingy & Organic.Buah! = 0
lebih mudah dibaca daripada
thingy & (Organic.Apple | Organic.Orange | Organic.Pear)! = 0
Inti dari Flag enums adalah untuk memungkinkan Anda menggabungkan beberapa status. Anda baru saja menjadikan ini lebih bermanfaat dan mudah dibaca. Anda menyampaikan konsep buah dalam kode Anda, saya tidak perlu mencari tahu sendiri bahwa apel dan jeruk dan pir berarti buah.
Berikan orang ini poin brownies!
sumber
thingy is Fruit
lebih mudah dibaca daripada keduanya.