我知道这反对枚举的想法,但是可以在C#/ Java中扩展枚举吗?我的意思是“扩展”,无论是为枚举添加新值,还是从现有枚举继承的OO意义。
我认为它在Java中是不可能的,因为它最近才得到它们(Java 5?)。 C#似乎对想要做疯狂事情的人更宽容,所以我认为这可能是某种方式。据推测,它可以通过反射被黑客攻击(并不是说你实际上每个人都使用那种方法)?
我不一定对实施任何给定的方法感兴趣,只是在我遇到它时激起了我的好奇心: - )
答案 0 :(得分:97)
你无法扩展枚举的原因是因为它会导致多态性问题。
假设您有一个包含值A,B和C的枚举MyEnum,并将值D扩展为MyExtEnum。
假设某个方法需要myEnum值,例如作为参数。提供MyExtEnum值应该是合法的,因为它是一个子类型,但是现在当你看到值是D时你要做什么?
要消除此问题,扩展枚举是非法的
答案 1 :(得分:40)
当内置枚举不够时,你可以用旧时尚的方式来制作你自己的。例如,如果要添加其他属性(例如,描述字段),则可以按如下方式添加:
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
}
}
诀窍是确保构造函数是私有的(如果你想继承,则保护它),并且你的实例是静态的。
答案 2 :(得分:38)
你走的路是错误的:枚举的子类会有更少的条目。
在伪代码中,想一想:
enum Animal { Mosquito, Dog, Cat };
enum Mammal : Animal { Dog, Cat }; // (not valid C#)
任何可以接受动物的方法都应该能够接受哺乳动物,但不能相反。子类化是为了使某些更具体,而不是更一般。这就是为什么“对象”是类层次结构的根。同样,如果枚举是可继承的,那么枚举层次结构的假设根将具有所有可能的符号。
但不,C#/ Java不允许子枚举,AFAICT,尽管它有时会非常有用。这可能是因为他们选择将Enums实现为int(如C)而不是实体符号(如Lisp)。 (上面,(动物)1代表什么,(哺乳动物)1代表什么,它们是相同的值?)
但是,您可以编写自己的枚举类(使用不同的名称)来提供此功能。使用C#属性,它甚至可能看起来很不错。
答案 3 :(得分: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 :(得分:3)
您可以使用.NET反射在运行时从现有枚举中检索标签和值(Enum.GetNames()
和Enum.GetValues()
是您将使用的两种特定方法),然后使用代码注入用这些元素和一些新元素创建一个新元素。这似乎与“继承现有的枚举”有点类似。
答案 5 :(得分:2)
如果你回到源代码并进行编辑,添加枚举是很常见的事情,任何其他方式(继承或反射,如果可能的话)很可能会回来并在你升级时遇到你库和他们引入相同的枚举名称或相同的枚举值 - 我已经看到大量的低级代码,其中整数与二进制编码匹配,在那里你会遇到问题
理想情况下,引用枚举的代码应该写为仅等于(或开关),并且不要期望枚举设置为const
,以试图将来证明答案 6 :(得分:2)
如果你的意思是在基类意义上扩展,那么在Java ... no。
但是你可以扩展枚举值以获得属性和方法,如果这就是你的意思。
例如,以下使用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;
}
}
答案 7 :(得分:2)
我没有看到其他人提到这一点,但枚举的序数值很重要。例如,使用grails将枚举保存到数据库时,它使用序数值。如果你能以某种方式扩展枚举,你的扩展的序数值是什么?如果你在多个地方扩展它,你怎么能保留这些序数的某种秩序?序数值的混乱/不稳定将是一件坏事,这可能是语言设计者没有触及这一点的另一个原因。
如果您是语言设计者,另一个困难是,如何保留应该返回所有枚举值的values()方法的功能。你会怎样调用它以及如何收集所有值?
答案 8 :(得分:1)
前一段时间看了一篇关于Java的帖子,请查看http://www.javaspecialists.eu/archive/Issue161.html。
答案 9 :(得分:0)
您无法继承/扩展枚举,您可以attributes使用declare a description。如果你正在寻找一个整数值,那就是内置的。
答案 10 :(得分:0)
嗯 - 据我所知,这是不可能做到的 - 枚举是在设计时编写的,用来方便程序员。
我很确定在编译代码时,等效值将替换枚举中的名称,从而删除枚举的概念,并因此删除扩展它的能力。
答案 11 :(得分: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;
这不会导致上面提到的任何问题,所以如果可能的话会很好。
答案 12 :(得分:0)
就java而言,它是不允许的,因为向枚举添加元素将有效地创建超类而不是子类。
考虑:
enum Person (JOHN SAM}
enum Student extends First {HARVEY ROSS}
多态性的一般用例是
Person person = Student.ROSS; //not legal
这显然是错误的。
答案 13 :(得分:0)
有些时候甚至我想做这样的事情,并发现枚举扩展会消化很多基本概念......(不仅仅是多态性)
但是如果枚举是在外部库中声明的话,你可能还需要做 请记住,使用此枚举扩展程序时应特别小心......
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;
}
}
答案 14 :(得分: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 );
在以下位置查看所有答案:Enum "Inheritance"