When and how should I use enumeration classes rather than enums?

筅森魡賤 提交于 2021-02-18 05:22:30

问题


A developer at work recently started using a class pattern instead of enums in places where enums would usually fit. Instead, he uses something similar to that below:

internal class Suit
{
    public static readonly Suit Hearts = new Suit();
    public static readonly Suit Diamonds = new Suit();
    public static readonly Suit Spades = new Suit();
    public static readonly Suit Clubs = new Suit();
    public static readonly Suit Joker = new Suit();
    private static Suit()
    {

    }
    public static bool IsMatch(Suit lhs, Suit rhs)
    {
        return lhs.Equals(rhs) || (lhs.Equals(Joker) || rhs.Equals(Joker));
    }
}

His reasoning is that it invisibly looks like an enumeration, but allows him to contain the methods relating to the numeration (like the IsMatch above) to be contained within the enumeration itself.

He called this an Enumeration class, but it's not something I've ever seen before. I wondered what the advantages and disadvantages were and where I could find out more information?

Thanks

Edit: Another advantage he described was being able to add a specific ToString() implementation for the enumeration.


回答1:


The main advantage of enums is that they are basically integers with some named values, as such they are inherently portable and serializable. Arithmetic and logical operations are also faster on enums.

Enumeration classes are used when you need an opaque value which has extra state information. For instance a generic Data Access Layer could have as an interface like:

public static class Dal
{
    public static Record Query(Operation operation, Parameters parameters);
}

var result = Dal.Query(Operation.DoSomething, new DoSomethingParameters {...});

To the users of the Dal Operation is just an enumeration of the operations available, but it could contain the connection string, the SQL statement or stored procedure and any other data needed by the generic Dal.

Another "common" use is for public modes in a system (a state or a strategy). From a user perspective the mode is an opaque value, but it may contain information or internal functionality crucial to the implementation of the system. A contrived example:


public class TheSystem
{
   public SystemMode Mode;
   public void Save()
   {
      Mode.Save();
   }
   public SystemDocument Read()
   {
      return Mode.Read();
   }
}

public abstract class SystemMode
{
   public static SystemMode ReadOnly = new ReadOnlyMode();
   public static SystemMode ReadWrite = new ReadWriteMode();

   internal abstract void Save();
   internal abstract SystemDocument Read();

   private class ReadOnlyMode : SystemMode
   {
      internal override void Save() {...}
      internal override SystemDocument Read() {...}
   }

   private class ReadWriteMode : SystemMode
   {
      internal override void Save() {...}
      internal override SystemDocument Read() {...}
   }
}


TheSystem.Mode = SystemMode.ReadOnly;

I don't think just having an IsMatch static method warrants not using simple enums. Something very similar could be achieved with extension methods in this case.




回答2:


Enums are just fine in lots of scenarios, but quite poor in others. Usually I find some issues with Enums :

  • Behavior related to the enumeration gets scattered around the application
  • New enumeration values require shotgun surgery
  • Enumerations don’t follow the Open-Closed Principle

With enumeration behavior scattered around, we can never bring it back to the source type, because enumeration types can’t have any behavior (or state for that matter).

In the other hand with an Enumeration Class:

All of the variations of each enumeration type can be pushed down not only to the enumeration class, but to each specific subtype.

Enumerations work well in a variety of scenarios, but can break down quickly inside your domain model. Enumeration classes provide much of the same usability, with the added benefit of becoming a destination for behavior.

Switch statements are no longer necessary, as I can push that variability and knowledge where it belongs, back inside the model. If for some reason I need to check specific enumeration class values, the option is still open for me. This pattern shouldn’t replace all enumerations, but it’s nice to have an alternative.

can read on that here




回答3:


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.

reference form a old post from this forum : When to use enums, and when to replace them with a class with static members?



来源:https://stackoverflow.com/questions/16507323/when-and-how-should-i-use-enumeration-classes-rather-than-enums

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!