通过继承扩展枚举


85

我知道这有点违反枚举的想法,但是可以在C#/ Java中扩展枚举吗?我的意思是“扩展”,既是向枚举添加新值,也就是从现有枚举继承的OO。

我认为这在Java中是不可能的,因为它是最近才得到的(Java 5?)。但是,C#对于希望做疯狂事情的​​人似乎更宽容,所以我认为这可能是有可能的。大概可以通过反思来破解它(不是每个人都实际使用该方法)?

我不一定对实现任何给定的方法感兴趣,它只是在引起我好奇时才引起我的好奇:-)


这回答了你的问题了吗?枚举“继承”
T.Todua

Answers:


103

您不能扩展Enums的原因是,它将导致多态性问题。

假设您有一个枚举MyEnum,其值分别为A,B和C,并将其值D扩展为MyExtEnum。

假设某个方法在某处期望myEnum值,例如作为参数。提供MyExtEnum值应该是合法的,因为它是子类型,但是现在当值变成D时您将要做什么?

为了消除此问题,扩展枚举是非法的


4
实际上,原因仅仅是它没有任何意义。您提到的问题,即客户端代码接收到意外的常量,在当前的实现中仍然存在-实际上,编译器不允许您switch提供枚举值而不提供default大小写或引发异常。而且,即使这样做,运行时枚举也可能来自单独的编译
Raffaele 2014年

@Raffaele我刚刚尝试过,至少在Java 7中,您可以打开枚举而无需使用default大小写。
丹丹

@Dathan你绝对是对的!正如它写的那样,这是完全错误的-也许我应该删除它?我在想这种情况,当您使用aswitch来提供所需的值时,例如本地的return
缩写

3
瑞克,这不是个人的。但这几乎是最糟糕的原因!多态和枚举……DayOfWeek a = (DayOfWeek) 1; DayOfWeek b = (DayOfWeek) 4711; Console.WriteLine(a + ", " + b);
Bitterblue

41

当内置枚举还不够时,您可以按照旧的方式进行操作,然后自己制作。例如,如果您想添加其他属性,例如描述字段,则可以按照以下步骤进行操作:

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");
}

然后,您可以将其像枚举一样对待:

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

诀窍是确保构造函数是私有的(如果要继承则受保护),并且实例是静态的。


我不是C#人员,但是您是否不想在其中加入一些最终的内容(或密封的/常量/任何内容)?
汤姆·哈汀-钉路

1
我不相信 或者,至少不是要从此类继承以添加新的“枚举”值的情况。最终和密封防止继承IIRC。
alastairs

4
@alastairs我想他的意思添加finalpublic static Action DoIt = new Action("Do it", "This does things");线,而不是类。
暗恋

1
如添加readonly,ala:public static readonly Action DoIt = new Action("Do it", "This does things");
ErikE

40

您走错了路:枚举的子类将具有较少的条目。

用伪代码考虑:

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

任何可以接受动物的方法都应该能够接受哺乳动物,但不能接受。子类化是为了使某些内容更具体而不是更一般。这就是为什么“对象”是类层次结构的根源。同样,如果枚举是可继承的,则枚举层次结构的假想根将具有所有可能的符号。

但是不,C#/ Java不允许使用子枚举AFAICT,尽管有时它确实很有用。可能是因为他们选择将Enums实现为int类型(如C)而不是内部符号(如Lisp)。(以上,(动物)1代表什么,(哺乳动物)1代表什么,它们的值相同吗?)

不过,您可以编写自己的类似枚举的类(使用其他名称)。使用C#属性,它甚至看起来还不错。


3
有趣的分析。从来没有想过这种方式!
Nerrve

有趣的是,但我认为这是错误的。暗示子类中的类型应该更少,就动物分类树而言,而不是代码。当您使用代码订阅时,方法永远不会减少。成员变量永远不会减少。您的论点似乎不适用于软件,不适用于动物分类。
Kieveli 2013年

4
@Kieveli,您的分析是错误的。添加成员对对象的影响与添加其可能值的集合不同。这不是Ken编造的。有明确而精确的计算机科学规则可以解释其为何如此工作。尝试搜索协方差和协方差(也不只是学术自慰,它还将帮助您了解函数签名和容器之类的规则)。
jwg

@Kieveli假设您有一个'StreamWriter'类(接受流-写流)。您将创建“ TextWriter:StreamWriter”(获取流-写入文本)。现在,您创建“ HtmlWriter:TextWriter”(使用流-编写漂亮的html)。现在,HtmlWriter显然具有更多的成员,方法等等。现在尝试从声卡中获取流,并使用HtmlWriter将其写入文件:)
evictednoise

本示例是人为设计的,不能证明扩展类中永远不应有更多的枚举值。枚举VehicalParts {许可证,速度,容量};枚举CarParts {SteeringWheel,Winshield};+车辆所具备的一切
Bernoulli Lizard

12

枚举应该代表所有可能值的枚举,因此扩展而不是违背了这一思想。

但是,在Java(可能是C ++ 0x)中可以执行的是使用接口而不是枚举类。然后,将标准值放入实现该功能的枚举中。显然,您不会使用java.util.EnumSet之类的东西。这是“更多NIO功能”中采用的方法,应该在JDK7中使用。

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


public enum WTFResults implements Result {
    FILE_NOT_FOUND
}

4

您可以使用.NET反射在运行时检索从现有的枚举的标签和值(Enum.GetNames()Enum.GetValues()你会使用这两种具体方法),然后使用代码注入来创建一个新的与这些元素加上一些新的问题。这似乎有点类似于“从现有枚举继承”。


2

如果您返回源代码并进行编辑,则添加枚举是很常见的事情,当您升级库时,任何其他方式(继承或反射,如果可能的话)都可能会回来并打您。他们引入了相同的枚举名称相同的枚举值-我看到了很多低级代码,其中整数与二进制编码匹配,您会遇到问题

理想情况下,应将引用枚举的代码编写为仅等于(或切换),并通过不期望枚举设置为const来尝试作为将来的证明


2

如果您的意思是在基类的意义上扩展,那么在Java中……不。

但是,如果这就是您的意思,则可以扩展枚举值以具有属性和方法。

例如,以下代码使用Bracket枚举:

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;
    }        
}

这听起来像.NET中的值类型(结构)。我不知道您可以用Java做到这一点。
alastairs

2

我没有看到其他人提及此事,但枚举的顺序值很重要。例如,对于grails,将枚举保存到数据库时,它将使用序数值。如果您可以通过某种方式扩展枚举,则扩展的序数值是什么?如果您将其扩展到多个地方,如何保留这些常规的某种顺序?序数值中的混乱/不稳定性将是一件坏事,这可能是语言设计人员未曾涉及此问题的另一个原因。

如果您是语言设计人员,则另一个困难是,如何保留values()方法的功能,该方法应该返回所有枚举值。您将如何调用它,它将如何收集所有值?




0

嗯-据我所知,这无法完成-枚举是在设计时编写的,用于方便程序员。

我很确定,在编译代码时,将使用等效值替换枚举中的名称,从而消除了枚举的概念,并因此消除了对其进行扩展的能力。


0

我希望能够将值添加到C#枚举中,这些值是现有值的组合。例如(这就是我想要做的):

AnchorStyles定义为

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

我想添加一个AnchorStyles.BottomRight = Right + Bottom,所以不用说

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

我可以说

my_ctrl.Anchor = AnchorStyles.BottomRight;

这不会引起上面提到的任何问题,因此,如果可能的话,会很好。


0

不久前,我什至想做这样的事情,发现枚举扩展会破坏很多基本概念……(不仅仅是polymorphisim)

但是,如果在外部库中声明了枚举,您仍然可能需要做,并且请记住,使用此枚举扩展时应格外小心。

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而言,这是不允许的,因为将元素添加到枚举将有效地创建超类而不是子类。

考虑:

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

多态的一般用例是

 Person person = Student.ROSS;   //not legal

这显然是错误的。


0

一种临时/本地解决方法,当您只希望非常本地/一次性使用时:

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   );

在以下位置查看所有答案:枚举“继承”

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.