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
/ is
kontrollerine) 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
/ is
denetimler 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 instanceof
yerine, 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 Function
ve Procedure
muhtemelen 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 PropertyGet
davranış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
/ is
kontrollere 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
/ is
bir 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
/ PropertySet
en 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, Property
tanı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 Type
bunun 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ğerx
bir alt türü olup olmadığını belirlemeky
istersem, muhtemelen bunux.IsSubtypeOf(y)
değil olarak yazmak isteyeceğimx && y == y
.