TL; DR En alta kaydırın.
Gördüğüm kadarıyla, C # üzerine yeni bir dil uyguluyorsunuz. Sıralamalar, programın ağaç temsiline eklenecek düğümlere uygulanmış gibi görünen bir tanımlayıcının türünü (veya adı olan ve yeni dilin kaynak kodunda görünen herhangi bir şeyi) belirtiyor gibi görünüyor.
Bu özel durumda, farklı tipteki düğümler arasında çok az polimorfik davranış vardır. Başka bir deyişle, ağacın çok farklı tiplerde (varyantlar) düğümler içerebilmesi gerekirken, bu düğümlerin gerçek ziyareti temelde dev bir if-then-else zincirine (veya instanceof/ iskontrollerine) başvurur . Bu dev denetimler muhtemelen proje boyunca birçok farklı yerde gerçekleşecek. Numaralandırmaların yararlı görünmesinin nedeni budur veya en azından instanceof/ isdenetimler kadar yararlıdır .
Ziyaretçi kalıbı yine de faydalı olabilir. Başka bir deyişle, dev zincirinin yerine kullanılabilecek çeşitli kodlama stilleri vardır instanceof. Bununla birlikte, çeşitli avantajlar ve dezavantajlar hakkında bir tartışma yapmak isterseniz, numaralandırmalar hakkında tartışmak instanceofyerine, projenin en çirkin zincirinden bir kod örneği göstermeyi seçersiniz.
Bu, sınıfların ve miras hiyerarşisinin yararlı olmadığı anlamına gelmez. Tam tersi. Her bildiri türünde (her bildirimin bir Nameözelliğe sahip olması gerçeğinin yanı sıra) çalışan herhangi bir polimorfik davranış olmasa da , yakın kardeşler tarafından paylaşılan çok sayıda zengin polimorfik davranış vardır. Örneğin Functionve Proceduremuhtemelen bazı davranışları paylaşın (her ikisi de çağrılabilir ve yazılan girdi bağımsız değişkenlerinin bir listesini kabul eder) ve PropertyGetdavranışları kesinlikle devralır Function(her ikisinde de a vardır ReturnType). Dev if-then-else zinciri için numaralandırmalar veya miras denetimleri kullanabilirsiniz, ancak parçalara ayrılmış polimorfik davranışlar yine de sınıflarda uygulanmalıdır.
Aşırı kullanım instanceof/ iskontrollere karşı birçok çevrimiçi tavsiye var . Performans bunun nedenlerinden biri değildir. Aksine, neden sanki organik olarak, uygun polimorfik davranışları keşfetme gelen programcı önlemektir instanceof/ isbir koltuk değneği. Ancak sizin durumunuzda, başka seçeneğiniz yoktur, çünkü bu düğümlerin ortak noktası çok azdır.
Şimdi bazı somut öneriler.
Yaprak olmayan gruplamaları temsil etmenin birkaç yolu vardır.
Orijinal kodunuzun aşağıdaki alıntısını karşılaştırın ...
[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,
}
bu değiştirilmiş sürüme:
[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
}
Bu değiştirilmiş versiyon, bitlerin somut olmayan bildirim türlerine tahsis edilmesini önler. Bunun yerine, somut olmayan bildirim türlerinin (bildiri türlerinin soyut gruplamaları) tüm çocuklarında bitsel veya bitlerin birliği olan enum değerleri vardır.
Bir uyarı var: tek bir çocuğu olan soyut bir bildiri türü varsa ve soyut olanı (ebeveyn) somut olandan (çocuk) ayırt etmeye ihtiyaç varsa, soyut olanın hala kendi bitine ihtiyacı olacaktır. .
Bu sorunun özgüdür Bir ihtar: Bir Property(sadece o kodda nasıl kullanıldığını görmeden, adını görünce) başlangıçta bir tanımlayıcı, ama gecirmislerdir olabilir PropertyGet/ PropertyLet/ PropertySeten kısa sürede bunu nasıl kullanıldığını görüyoruz kodda. Başka bir deyişle, ayrıştırma işleminin farklı aşamalarında, Propertytanımlayıcıyı "bu ad bir özelliğe başvuruyor" olarak işaretlemeniz ve daha sonra "bu kod satırı bu özelliğe belirli bir şekilde erişiyor" olarak değiştirmeniz gerekebilir .
Bu uyarıyı çözmek için iki dizi numaraya ihtiyacınız olabilir; bir enum, bir adın (tanımlayıcı) ne olduğunu belirtir; başka bir enum, kodun ne yapmaya çalıştığını gösterir (örneğin, bir şeyin gövdesini bildirmek; bir şeyi belirli bir şekilde kullanmaya çalışmak).
Her bir enum değeri hakkındaki yardımcı bilgilerin bunun yerine bir diziden okunup okunamayacağını düşünün.
Bu öneri diğer önerilerle karşılıklı olarak münhasırdır, çünkü iki güç değerini küçük negatif olmayan tamsayı değerlerine dönüştürmeyi gerektirir.
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), ...
Sürdürülebilirlik sorunlu olacaktır.
C # türleri (kalıtım hiyerarşisindeki sınıflar) ile numaralandırma değerleriniz arasında bire bir eşleme olup olmadığını kontrol edin.
(Alternatif olarak, türlerle bire bir eşleme sağlamak için numaralandırma değerlerinizi düzenleyebilirsiniz.)
C # 'ta birçok kütüphane Type object.GetType()iyi veya kötü için şık yöntemi kötüye kullanır.
Numaralandırmayı bir değer olarak depoladığınız her yerde kendinize Typebunun yerine değer olarak depolayıp depolayamayacağınızı sorabilirsiniz .
Bu hileyi kullanmak için, iki salt okunur karma tabloyu başlatabilirsiniz:
// 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
...
};
Sınıfları ve miras hiyerarşisini önerenler için son onaylama ...
Numaralandırmaların kalıtım hiyerarşisine bir yaklaşım olduğunu görebildiğinizde , aşağıdaki öneriler geçerlidir:
- Önce miras hiyerarşinizi tasarlayın (veya geliştirin),
- Ardından geri dönün ve numaralarınızı bu miras hiyerarşisine yaklaşacak şekilde tasarlayın.
DeclarationType. Eğerxbir alt türü olup olmadığını belirlemekyistersem, muhtemelen bunux.IsSubtypeOf(y)değil olarak yazmak isteyeceğimx && y == y.