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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2119714/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 23:40:38  来源:igfitidea点击:

When to use enums, and when to replace them with a class with static members?

c#enumspolymorphismstatic-members

提问by stakx - no longer contributing

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 Colorwas an enum, but not if Colorwere the above class:

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

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

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

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

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

采纳答案by Chris Haas

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:

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

[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.Colormembers 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:

然而,静态类成员对于常用对象非常有用。该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;
}

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.

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

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?

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

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.

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

回答by cmw

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.

然后 P/Invoke 的编组可读性较差,因为需要进行转换才能将枚举转换为适当的值。

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

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

回答by stakx - no longer contributing

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

我在此期间发现的一些事情,如果其他人有兴趣:

  • switchblocks won't workwith an enum-as-class.

  • User empimentioned 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.

  • switch块不适用于 enum-as-class。

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

回答by Anton

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 Bin Jon Skeet's blog article "Enhanced enums in C#".

它基于John BJon Skeet 的博客文章“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.

我的意思是,您很难创建 DerivedEnumClass1 的变量,并且必须从它的枚举集合中存储一个值,该值实际上是该变量中的 BaseEnumClass1 类型,而没有变通方法。

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.

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

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

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

回答by organicveggie

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 Enumpattern. Unfortunately, the latest release of the book targets Java > 1.5, so it uses Java enums instead.

是的,Joshua Bloch 的“Effective Java”的原始版本在 Java 1.5 之前发布并且对 Java 中的枚举的本机支持展示了这种Typesafe Enum模式。不幸的是,这本书的最新版本针对 Java > 1.5,因此它使用 Java 枚举代替。

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

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

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

I can't speak for other languages though.

不过我不会说其他语言。

回答by Alex P

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

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

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?

有一个非常相似的重构,名为用策略模式替换枚举。好吧,在您的情况下,它不是很完整,因为您的颜色是被动的并且不像策略那样起作用。但是,为什么不把它看作一个特例呢?