何时使用枚举,何时用具有静态成员的类替换它们?

发布于 2024-08-19 08:54:30 字数 749 浏览 8 评论 0原文

我最近想到以下(示例)枚举

enum Color
{
    Red,
    Green,
    Yellow,
    Blue
}

......可以用看似更类型安全的类替换:

class Color
{
    private Color() { }

    public static readonly Color   Red      = new Color();
    public static readonly Color   Green    = new Color();
    public static readonly Color   Yellow   = new Color();
    public static readonly Color   Blue     = new Color();
}

使用“类型安全”,我的意思是如果 Color 则以下语句将起作用 是一个枚举,但如果 Color 是上面的类,则不是:

var nonsenseColor = (Color)17;    // works if Color is an enum

两个问题:

1) 此模式是否有一个被广泛接受的名称(将枚举替换为类型安全的类)?

2) 在什么情况下应该使用枚举,什么时候使用类更合适?

It recently occured to me that the following (sample) enumeration...

enum Color
{
    Red,
    Green,
    Yellow,
    Blue
}

... could be replaced with a seemingly more type-safe class:

class Color
{
    private Color() { }

    public static readonly Color   Red      = new Color();
    public static readonly Color   Green    = new Color();
    public static readonly Color   Yellow   = new Color();
    public static readonly Color   Blue     = new Color();
}

With "type-safe", I mean that the following statement would work if Color was an enum, but not if Color were the above class:

var nonsenseColor = (Color)17;    // works if Color is an enum

Two questions:

1) Is there a widely accepted name to this pattern (replacing an enum with a type-safe class)?

2) In which cases should one use enums, and when would a class be more appropriate?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

╰つ倒转 2024-08-26 08:54:30

枚举非常适合轻量级状态信息。例如,您的颜色枚举(不包括蓝色)非常适合查询交通灯的状态。真实的颜色以及颜色的整个概念及其所有包袱(alpha、颜色空间等)并不重要,重要的是灯处于哪个状态。另外,稍微改变你的枚举来表示交通灯的状态:

[Flags()]
public enum LightColors
{
    unknown = 0,
    red = 1,
    yellow = 2,
    green = 4,
    green_arrow = 8
}

当前灯光状态可以设置为:

LightColors c = LightColors.red | LightColors.green_arrow;

并查询为:

if ((c & LightColors.red) == LightColors.red)
{
    //Don't drive
}
else if ((c & LightColors.green_arrow) == LightColors.green_arrow)
{
    //Turn
}

静态类颜色成员将能够支持这种多重状态,而无需额外的功能。

然而,静态类成员对于常用对象来说非常有用。 System.Drawing.Color 成员是很好的例子,因为它们代表具有模糊构造函数的已知名称颜色(除非您知道十六进制颜色)。如果它们被实现为枚举,那么每次您想要使用该值作为颜色时,您都必须执行类似的操作:

colors c = colors.red;
switch (c)
{
    case colors.red:
        return System.Drawing.Color.FromArgb(255, 0, 0);
        break;
    case colors.green:
        return System.Drawing.Color.FromArgb(0,255,0);
        break;
}

因此,如果您有一个枚举并发现您不断地执行 switch/case/if/else/whatever要派生对象,您可能需要使用静态类成员。如果您只是查询某物的状态,我会坚持使用枚举。另外,如果您必须以不安全的方式传递数据,则枚举可能比对象的序列化版本更好地生存。

编辑:
@stakx,我认为你在回应@Anton的帖子时也偶然发现了一些重要的东西,那就是复杂性或更重要的是,它对谁来说是复杂的?

从消费者的角度来看,我非常喜欢 System.Drawing.Color 静态类成员,而不是必须编写所有这些。然而,从制片人的角度来看,必须编写所有这些内容将是一件痛苦的事情。因此,如果其他人要使用您的代码,您可能会通过使用静态类成员为他们节省很多麻烦,即使您可能需要花费 10 倍的时间来编写/测试/调试。但是,如果只有您,您可能会发现根据需要使用枚举和转换/转换会更容易。

Enums are great for lightweight state information. For example, your color enum (excluding blue) would be good for querying the state of a traffic light. The true color along with the whole concept of color and all its baggage (alpha, color space, etc) don't matter, just which state is the light in. Also, changing your enum a little to represent the state of the traffic light:

[Flags()]
public enum LightColors
{
    unknown = 0,
    red = 1,
    yellow = 2,
    green = 4,
    green_arrow = 8
}

The current light state could be set as:

LightColors c = LightColors.red | LightColors.green_arrow;

And queried as:

if ((c & LightColors.red) == LightColors.red)
{
    //Don't drive
}
else if ((c & LightColors.green_arrow) == LightColors.green_arrow)
{
    //Turn
}

Static class color members would be able to support this multiple state without extra functionality.

However, static class members are wonderful for commonly used objects. The System.Drawing.Color members are great examples as they represent a known-name colors that have obscure constructors (unless you know your hex colors). If they were implemented as enums you would have to do something like this every time you wanted to use the value as a color:

colors c = colors.red;
switch (c)
{
    case colors.red:
        return System.Drawing.Color.FromArgb(255, 0, 0);
        break;
    case colors.green:
        return System.Drawing.Color.FromArgb(0,255,0);
        break;
}

So if you've got an enum and find that your constantly doing a switch/case/if/else/whatever to derive an object, you might want to use static class members. If you're only querying the state of something, I'd stick with enums. Also, if you have to pass data around in an unsafe fashion enums will probably survive better than a serialized version of your object.

Edit:
@stakx, I think you stumbled on something important, too in response to @Anton's post and that is complexity or more importantly, who is it complex for?

From a consumer's standpoint, I would immensely prefer System.Drawing.Color static class members over having to write all of that. From a producer's standpoint, however, it would be a pain to have to write all of that. So if other people are going to be using your code you might be saving them a lot of trouble by using static class members even though it might take you 10 times as long to write/test/debug. However, if its just you you might find it easier to just use enums and cast/convert as needed.

黎夕旧梦 2024-08-26 08:54:30

同时我发现了一些事情,如果其他人感兴趣的话:

  • switch 块无法与枚举类一起使用

  • 用户empi提到了上述枚举类示例与Java枚举的相似性。似乎在Java世界中,有一种公认的模式,称为Typesafe Enum;显然,这种模式可以追溯到 Joshua Bloch 的书 Effective Java

Some things I found in the meantime, if anyone else is interested:

  • switch blocks won't work with an enum-as-class.

  • User empi mentioned the similarity of the above enum-as-class sample to Java enums. It seems that in the Java world, there is a recognised pattern called the Typesafe Enum; apparently this pattern goes back to Joshua Bloch's book Effective Java.

謌踐踏愛綪 2024-08-26 08:54:30

事实上,我在工作中一直在为类似的事情而苦苦挣扎。

它基于 发布的示例作者:John B Jon Skeet 的博客文章“C# 中的增强枚举”< /a>.

我无法在没有很多丑陋的情况下正常工作的是通过派生基本枚举类并添加派生类型的附加静态字段来扩展枚举。

如果您检查该博客中发布的基础知识,您会注意到派生类将共享一组具有严重打字问题的通用值,并且当在两个不同的其他类中派生某个枚举基类时,它们的值集将位于也是同一个集合。

我的意思是,您很难创建 DerivedEnumClass1 变量,并且必须在该变量中存储其枚举集合中的值,该值实际上是 BaseEnumClass1 类型,而无需解决方法。

另一个问题是我们经常使用的 Xml 序列化。我使用两个通用类作为业务对象上枚举变量的数据类型来解决这个问题:一个表示单个枚举值,另一个表示一组枚举值,我用它们来模仿标志枚举行为。它们处理存储在它们所表示的属性中的实际值的 xml 序列化和反序列化。以这种方式重新创建位标志行为所需的只是几个运算符重载。

这些是我遇到的一些主要问题。

总而言之,我对最终结果不太满意,里面有一些难闻的东西,但它现在可以完成工作。

为什么我们的首席架构师决定仍然尝试让它发挥作用,是因为有可能拥有实际的类并通过各种行为(通用的或特定的)扩展它们。到目前为止,我还没有看到很多我无法提供的扩展方法,但我们可能会遇到一些问题。

这里没有任何代码,我周末无法使用它,否则我可以展示我用它去了哪里。虽然不是很漂亮...:o

I have actually been struggling with something like this quite a bit at work.

It was based on an example that was posted by John B in Jon Skeet's blog article "Enhanced enums in C#".

What I did not get to work properly without a LOT of ugliness was extending an enumeration by deriving a base enumeration class and adding additional static fields of the deriving type.

If you check the basics that are posted in that blog you'll notice that deriving classes will share a common set of values which have serious typing issues and when a certain enumeration base class is derived in two different other classes their value sets will be in the same collection too.

I mean, you'd be hard pressed to create a variable of DerivedEnumClass1 and having to store a value from it's enumeration collection which is actually of type BaseEnumClass1 in that variable without workarounds.

Another problem was Xml Serialization which we use a lot. I got around that using two generic classes as datatypes on enumeration variables on our business objects: one that represented a single enumeration value and one that represented a set of enumeration values which I used to mimic flags enumeration behaviour. They handled xml serialization and deserialization of the actual values that were stored in the properties they were representing. A couple operator overloads were all that was necessary to recreate bitflag behaviour this way.

Those are some of the major issure I ran into.

All in all I'm not very pleased with the end result, some smelly stuff in there but it does the job for now.

Why our chief architect decided to still try and get it to work was the possibilities of having actual classes and extending them with all kinds of behaviour, either generic or specific. Thus far I have not seen a lot I wouldn't have been able to provide with extension methods, but we might run into something.

Don't have any of the code here and I'm not gonna be able to get to it for the weekend otherwise I could have shown where I had gone with it. Not very pretty though... :o

执笔绘流年 2024-08-26 08:54:30

当我必须与一些遗留代码进行互操作时,我会使用类常量,这些遗留代码需要一些可能被或在一起的模糊位标志值。在这种情况下,枚举可能看起来像

public enum Legacy : ushort
{
  SomeFlag1 = 0x0001,
  SomeFlag2 = 0x0002,
  // etc...
}

那么,由于需要将枚举转换为适当的值,因此 P/Invoke 处的编组可读性较差。

如果它是 const 类变量,则不需要强制转换。

I would use the class constants when I have to interop with some legacy code that wants some obscure bit flag values that might be or'd together. In this case the enum might look like

public enum Legacy : ushort
{
  SomeFlag1 = 0x0001,
  SomeFlag2 = 0x0002,
  // etc...
}

Then the marshalling at the P/Invoke is less readable because of the casting needed to translate the enum to the appropriate value.

If it were a const class variable a cast wouldn't be needed.

寻找我们的幸福 2024-08-26 08:54:30

两种方法都是有效的。您应该根据情况进行选择。

我可以补充一点,枚举支持位操作作为标志(甚至有 [Flags] 属性来支持这种语义并从枚举生成漂亮的字符串)。

有一个非常相似的重构,名为用策略模式替换枚举。好吧,就您而言,它还不太完整,因为您的颜色是被动的并且不像策略。但是,为什么不把它看作一个特例呢?

Both approaches are valid. You should choose per case.

I can add that enums support bit operations as flags (there's even [Flags] attribute to support this semantics and produce pretty strings from enums).

There's a very similar refactoring named Replacing Enums with the Strategy Pattern. Well, in your case it's not quite complete, since your Color is passive and does not act like a strategy. However, why not think of it as a special case?

腻橙味 2024-08-26 08:54:30

是的,Joshua Bloch 所著的《Effective Java》的原始版本在 Java 1.5 之前发布,并且对 Java 中的枚举提供本机支持,它演示了 Typesafe Enum 模式。不幸的是,该书的最新版本针对的是 Java > 1.5,所以它使用Java枚举代替。

其次,在 Java 中不能从 int 转换为 enum。

Color nonsenseColor = (Color)17; // compile-error

但我不会说其他语言。

Yup, the original edition of "Effective Java" by Joshua Bloch, which was released prior to Java 1.5 and native support for enums in Java, demonstrated the Typesafe Enum pattern. Unfortunately, the latest release of the book targets Java > 1.5, so it uses Java enums instead.

Second, you can't cast from int to enum in Java.

Color nonsenseColor = (Color)17; // compile-error

I can't speak for other languages though.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文