Miras yoluyla bir numaralandırmayı genişletme


88

Bunun daha çok numaralandırma fikrine aykırı olduğunu biliyorum, ancak numaralandırmaları C # / Java'da genişletmek mümkün mü? Hem bir numaralandırmaya yeni değerler ekleme anlamında hem de mevcut bir numaralandırmadan miras alma OO anlamında "genişletmek" demek istiyorum.

Java'da bunun mümkün olmadığını düşünüyorum, çünkü onları oldukça yakın zamanda aldı (Java 5?). C # çılgınca şeyler yapmak isteyen insanları daha affedici görünüyor, bu yüzden bir şekilde mümkün olabileceğini düşündüm. Muhtemelen yansıtma yoluyla hacklenebilir (aslında herkesin bu yöntemi kullanması değil)?

Herhangi bir yöntemi uygulamakla mutlaka ilgilenmiyorum, sadece aklıma geldiğinde merakımı uyandırdı :-)


Bu sorunuzu yanıtlıyor mu? Enum "Kalıtım"
T.Todua

Yanıtlar:


105

Enums'i genişletememenizin nedeni, polimorfizm ile ilgili sorunlara yol açmasıdır.

A, B ve C değerlerine sahip bir MyEnum numaranız olduğunu ve MyExtEnum olarak D değeriyle genişlettiğinizi varsayalım.

Bir yöntemin bir yerde, örneğin bir parametre olarak bir myEnum değeri beklediğini varsayalım. Bir MyExtEnum değeri sağlamak yasal olmalıdır, çünkü bu bir alt türdür, ancak şimdi değerin D olduğu ortaya çıktığında ne yapacaksınız?

Bu sorunu ortadan kaldırmak için numaralandırmaları genişletmek yasa dışıdır


4
Aslında nedeni basitçe hiçbir anlam ifade etmiyor. Bahsettiğiniz sorun, yani beklemediği bir sabiti alan istemci kodu, mevcut uygulamada hala mevcuttur - aslında derleyici, switchbir defaultvaka sağlamadan veya bir istisna atmadan enum değerlerinde size izin vermez . Ve öyle olsa bile, çalışma zamanındaki sıralama ayrı bir derlemeden gelebilir
Raffaele

@Raffaele Bunu az önce denedim ve en azından Java 7'de bir defaultvaka veya fırlatma olmadan bir numaralandırmayı açabilirsiniz .
Dathan

@Dathan kesinlikle haklısın! Yazıldığı gibi, bu tamamen yanlış - belki onu kaldırmalıyım? switchBir yerelin baş harfi gibi gerekli bir değeri sağlamak için a kullandığınızda durumu düşünüyordum ya dareturn
Raffaele

3
Bu kişisel değil Rik. Ama bu şimdiye kadarki en kötü neden! Polimorfizm ve enum ...DayOfWeek a = (DayOfWeek) 1; DayOfWeek b = (DayOfWeek) 4711; Console.WriteLine(a + ", " + b);
Bitterblue

41

Yanlış yoldasınız: bir numaralandırmanın alt sınıfı daha az girdiye sahip olacaktır .

Sözde kodda şunları düşünün:

enum Animal { Mosquito, Dog, Cat };
enum Mammal : Animal { Dog, Cat };  // (not valid C#)

Bir Hayvanı kabul edebilecek herhangi bir yöntem, bir Memeliyi kabul edebilmelidir, ancak tersi olamaz. Alt sınıflandırma, bir şeyi daha genel değil, daha spesifik hale getirmek içindir. Bu nedenle "nesne", sınıf hiyerarşisinin köküdür. Benzer şekilde, numaralandırmalar miras alınabiliyorsa, enum hiyerarşisinin varsayımsal bir kökü olası her sembole sahip olacaktır.

Ama hayır, C # / Java, AFAICT gibi alt numaralandırmalara izin vermez, ancak bazen gerçekten yararlı olabilir. Muhtemelen, Enums'leri interned semboller (Lisp gibi) yerine int'ler olarak (C gibi) uygulamayı seçmelerinden kaynaklanıyor. (Yukarıda, (Hayvan) 1 neyi temsil ediyor ve (Memeli) 1 neyi temsil ediyor ve bunlar aynı değerde mi?)

Yine de bunu sağlayan kendi sıralama benzeri sınıfınızı (farklı bir adla) yazabilirsiniz. C # öznitelikleriyle güzel görünebilir bile.


3
ilginç analiz. asla böyle düşünmedim!
Nerrve

Intersting, ama bence yanlış. Bir alt sınıfta daha az tür olması gerektiğini ima etmek, ağaç sınıflandırması açısından mantıklı, ancak kod açısından değil. Kodda sublcass yaptığınızda, asla daha az yöntem yoktur. Asla daha az üye değişkeni yoktur. Argümanınız, hayvan sınıflandırmalarına olduğu gibi yazılıma uygulanmıyor gibi görünüyor.
Kieveli

4
@Kieveli, analizin yanlış. Bir üye eklemek, bir nesne üzerinde olası değerler kümesine eklemekle aynı etkiye sahip değildir. Bu Ken'in uydurduğu bir şey değil; bilgisayar biliminin neden bu şekilde çalıştığını açıklayan açık ve kesin kuralları vardır. Kovaryans ve kontravarlık aramayı deneyin (sadece akademik mastürbasyon değil, işlev imzaları ve kapsayıcılar gibi şeyler için kuralları anlamanıza yardımcı olacaktır).
jwg

@Kieveli Bir 'StreamWriter' sınıfınız olduğunu varsayalım (akış alır - akış yazar). 'TextWriter: StreamWriter' oluşturursunuz (akış alır - metin yazar). Şimdi 'HtmlWriter: TextWriter' oluşturuyorsunuz (akışı alır - oldukça html yazar). Şimdi, HtmlWriter açıkça daha fazla üyeye, yöntemlere vb. Sahip. Şimdi bir ses kartından akış almayı ve HtmlWriter kullanarak bunu dosyaya yazmayı deneyin :)
evictednoise

Bu örnek uydurma ve genişletilmiş sınıfta asla MORE enum değeri olmaması gerektiğini kanıtlamaz. enum VehicalParts {Lisans, Hız, Kapasite}; enum CarParts {SteeringWheel, Winshield}; + Vehical'in sahip olduğu her şey
Bernoulli Lizard

41

Yerleşik numaralandırmalar yeterli olmadığında, bunu eski moda şekilde yapabilir ve kendinizinkini oluşturabilirsiniz. Örneğin, ek bir mülk, örneğin bir açıklama alanı eklemek istiyorsanız, bunu aşağıdaki gibi yapabilirsiniz:

public class Action {
    public string Name {get; private set;}
    public string Description {get; private set;}

    private Action(string name, string description) {
        Name = name;
        Description = description;
    }

    public static Action DoIt = new Action("Do it", "This does things");
    public static Action StopIt = new Action("Stop It", "This stops things");
}

Daha sonra buna bir sıralama gibi davranabilirsiniz:

public void ProcessAction(Action a) {
    Console.WriteLine("Performing action: " + a.Name)
    if (a == Action.DoIt) {
       // ... and so on
    }
}

İşin püf noktası, yapıcının özel olduğundan (veya devralmak istiyorsanız korumalı olduğundan) ve örneklerinizin statik olduğundan emin olmaktır.


Ben bir C # insanı değilim, ama orada biraz son (veya mühürlü / sabit / her neyse) istemiyor musun?
Tom Hawtin - tackline

1
Ben inanmıyorum Veya, en azından, yeni "enum" değerleri eklemek için bu sınıftan miras almak isteyeceğiniz durum için değil. Nihai ve mühürlü mirası önleme IIRC.
alastairs

4
@alastairs Onun eklemek demekti düşünmek finaliçin public static Action DoIt = new Action("Do it", "This does things");oldukça sınıfın yerine, hat.
ezmek

1
Ekleyerek olduğu gibi readonly, ala:public static readonly Action DoIt = new Action("Do it", "This does things");
ErikE

12

Numaralandırmaların olası tüm değerlerin numaralandırılmasını temsil etmesi beklenir, bu nedenle genişletme, fikre aykırıdır.

Bununla birlikte, Java'da (ve muhtemelen C ++ 0x) yapabileceğiniz şey, enum sınıfı yerine bir arabirime sahip olmaktır. Ardından, özelliği uygulayan bir numaralandırmaya standart değerler koyun. Açıkçası java.util.EnumSet ve benzerlerini kullanamazsınız. Bu, JDK7'de olması gereken "daha fazla NIO özelliği" için alınan yaklaşımdır.

public interface Result {
    String name();
    String toString();
}
public enum StandardResults implements Result {
    TRUE, FALSE
}


public enum WTFResults implements Result {
    FILE_NOT_FOUND
}

4

Çalışma zamanında var olan bir numaralandırmadan ( Enum.GetNames()ve Enum.GetValues()kullanacağınız iki özel yöntemdir) etiketleri ve değerleri almak için .NET yansımasını kullanabilir ve ardından bu öğelerle birlikte yenilerini ve bazılarını içeren yeni bir tane oluşturmak için kod enjeksiyonunu kullanabilirsiniz. Bu, "mevcut bir enumdan miras almaya" biraz benziyor.


2

Numaralandırma eklemek, kaynak koda geri dönüp düzenlerseniz, yapılacak oldukça yaygın bir şeydir; başka herhangi bir yol (eğer mümkünse, miras veya yansıtma) büyük olasılıkla geri gelip kütüphanede bir yükseltme aldığınızda sizi vurur ve aynı enum adını veya aynı enum değerini tanıttılar - Tam sayı sayısının ikili kodlamayla eşleştiği, problemlerle karşılaşacağınız çok sayıda düşük düzeyli kod gördüm

İdeal olarak, kod referans numaralandırmaları yalnızca eşittir (veya anahtarlar) olarak yazılmalıdır ve enum kümesinin sabit olmasını beklemeyerek geleceğe yönelik kanıt olmaya çalışmalıdır.


2

Base sınıfı anlamında genişler demek istiyorsan, o zaman Java'da ... hayır.

Ancak, kastettiğiniz buysa, özelliklere ve yöntemlere sahip olmak için bir enum değerini genişletebilirsiniz.

Örneğin, aşağıdaki bir Bracket enum kullanır:

class Person {
    enum Bracket {
        Low(0, 12000),
        Middle(12000, 60000),
        Upper(60000, 100000);

        private final int low;
        private final int high;
        Brackets(int low, int high) {
            this.low = low;
            this.high = high;
        }

        public int getLow() {
            return low;
        }

        public int getHigh() {
            return high;
        }

        public boolean isWithin(int value) {
           return value >= low && value <= high;
        }

        public String toString() {
            return "Bracket " + low + " to " + high;
        }
    }

    private Bracket bracket;
    private String name;

    public Person(String name, Bracket bracket) {
        this.bracket = bracket;
        this.name = name;
    }

    public String toString() {
        return name + " in " + bracket;
    }        
}

Bu, .NET'te bir değer türü (yapı) gibi görünüyor. Bunu Java'da yapabileceğini bilmiyordum.
alastairs

2

Bundan başka kimsenin bahsettiğini görmedim, ancak bir numaralamanın sıra değeri önemlidir. Örneğin, grails ile bir numaralandırmayı veritabanına kaydettiğinizde, sıra değerini kullanır. Bir numaralandırmayı bir şekilde genişletebilseydiniz, uzantılarınızın sıra değerleri ne olurdu? Bunu birden fazla yere uzatırsanız, bu sıradanlara bir tür düzeni nasıl koruyabilirsiniz? Sıralı değerlerdeki kaos / istikrarsızlık kötü bir şeydir ve muhtemelen dil tasarımcılarının buna dokunmamasının bir başka nedeni budur.

Dil tasarımcısı olsaydınız bir başka zorluk da, tüm enum değerlerini döndürmesi beklenen values ​​() yönteminin işlevselliğini nasıl koruyabilirsiniz? Bunu neye çağırırsınız ve tüm değerleri nasıl toplar?




0

Hmmm - bildiğim kadarıyla bu yapılamaz - numaralandırmalar tasarım sırasında yazılır ve programcıya kolaylık sağlamak için kullanılır.

Kod derlendiğinde, eşdeğer değerlerin numaralandırmanızdaki adların yerine geçeceğinden, böylece bir numaralandırma kavramını ve (dolayısıyla) genişletme yeteneğini ortadan kaldıracağından oldukça eminim.


0

Mevcut değerlerin kombinasyonları olan C # numaralandırmalarına değer ekleyebilmek istiyorum. Örneğin (yapmak istediğim şey bu):

AnchorStyles şu şekilde tanımlanır:

public enum AnchorStyles { None = 0, Top = 1, Bottom = 2, Left = 4, Right = 8, }

ve bir AnchorStyles.BottomRight = Sağ + Alt eklemek istiyorum, yani

my_ctrl.Anchor = AnchorStyles.Right | AnchorStyles.Bottom;

Sadece söyleyebilirim

my_ctrl.Anchor = AnchorStyles.BottomRight;

Bu, yukarıda bahsedilen sorunların hiçbirine neden olmuyor, bu yüzden mümkün olsaydı iyi olurdu.


0

Bir süre önce bile böyle bir şey yapmak istedim ve enum uzantılarının birçok temel kavramı dile getireceğini keşfettim ... (Sadece polimorfisim değil)

Ama yine de, numaralandırma harici kitaplıkta bildirilmişse yapmanız gerekebilir ve bu enum uzantılarını kullanırken özel bir dikkat göstermeniz gerektiğini unutmayın ...

public enum MyEnum { A = 1, B = 2, C = 4 }

public const MyEnum D = (MyEnum)(8);
public const MyEnum E = (MyEnum)(16);

func1{
    MyEnum EnumValue = D;

    switch (EnumValue){
      case D:  break;
      case E:  break;
      case MyEnum.A:  break;
      case MyEnum.B:  break;
   }
}

0

Java söz konusu olduğunda buna izin verilmez çünkü bir numaralandırmaya eleman eklemek, bir alt sınıf yerine bir süper sınıf yaratır.

Düşünmek:

 enum Person (JOHN SAM}   
 enum Student extends Person {HARVEY ROSS}

Polimorfizmin genel kullanım durumu şöyle olacaktır:

 Person person = Student.ROSS;   //not legal

ki bu açıkça yanlıştır.


0

Yalnızca çok yerel / tek seferlik kullanım istediğinizde geçici / yerel bir çözüm :

enum Animals { Dog, Cat }
enum AnimalsExt { Dog = Animals.Dog, Cat= Animals.Cat,  MyOther}
// BUT CAST THEM when using:
var xyz = AnimalsExt.Cat;
MethodThatNeedsAnimal(   (Animals)xyz   );

Tüm yanıtları gör: Enum "Inheritance"

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.