C#:Enum反模式

有人一直在谈论Enums一般违反清洁代码原则,所以我正在寻找人们最喜欢的Enum反模式和替代解决方案。

例如,我见过这样的代码:

switch(enumValue) { case myEnum.Value1: // ... break; case myEnum.Value2: // ... break; } 

它比带有魔术字符串的switch语句更好一步,但这可能已经通过工厂,容器或其他模式更好地解决了。

甚至像这样的老派代码:

 if(enumValue == myEnum.Value1) { // ... } else if (enumValue == myEnum.Value2) { // ... } 

您对枚举有哪些其他反模式和更好的实现

我认为Enums非常有用。 我为Enum写了一些扩展,为它的使用增加了更多的价值

首先,有Description扩展方法

 public static class EnumExtensions { public static string Description(this Enum value) { var entries = value.ToString().Split(ENUM_SEPERATOR_CHARACTER); var description = new string[entries.Length]; for (var i = 0; i < entries.Length; i++) { var fieldInfo = value.GetType().GetField(entries[i].Trim()); var attributes = (DescriptionAttribute[])fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false); description[i] = (attributes.Length > 0) ? attributes[0].Description : entries[i].Trim(); } return String.Join(", ", description); } private const char ENUM_SEPERATOR_CHARACTER = ','; } 

这将允许我像这样定义en enum:

  public enum MeasurementUnitType { [Description("px")] Pixels = 0, [Description("em")] Em = 1, [Description("%")] Percent = 2, [Description("pt")] Points = 3 } 

并通过执行以下操作获取标签: var myLabel = rectangle.widthunit.Description() (不需要switch语句)。

如果rectangle.widthunit = MeasurementUnitType.Pixels ,它将返回“px”,如果rectangle.widthunit = MeasurementUnitType.Pixels ,它将返回“px,em” rectangle.widthunit = MeasurementUnitType.Pixels | MeasurementUnitType.Em

然后,有一个

  public static IEnumerable GetIntBasedEnumMembers(Type @enum) { foreach (FieldInfo fi in @enum.GetFields(BindingFlags.Public | BindingFlags.Static)) yield return (int)fi.GetRawConstantValue(); } 

这将让我遍历任何基于int的值的枚举并返回int值本身。

我发现这些在一个已经很有用的概念中非常有用。

我看到有两个 switch语句作为非OO设计的症状, 如本答案中进一步解释的那样 。

这不是答案,而是对Enum反模式列表的贡献。

在今天早上的代码审查期间,我遇到了类似于以下的案例,所有案例都在同一个class级。

两种情况:

  1. 喝酒前
  2. 喝完后

..

  public enum ListEnum { CategoryOne, CategoryTwo, CategoryThree, CategoryFour } public class UIELementType { public const string FactoryDomain = "FactoryDomain"; public const string Attributes = "Attributes"; } 

使用不反模式的枚举。 在一些关于重构的书中,这段代码用于演示如何用多态替换它。 当你在代码中过度使用枚举时就可以了。

这完全取决于你尝试用枚举做什么。

  1. 如果您试图阻止开发人员将魔术数字传递到您的操作中,并且您希望保持数据引用完整性与您的数据库完整,那么,是的! 使用T4-Templates(使用您的ORM)转到您的MeasurementUnitTypes表并生成一个枚举,其ID,Name和Description列与枚举’int,Enum_Name和Description属性相匹配(其他字段\数据的好方法可以枚举@danijels)如上所述。 如果向MeasurementUnitTypes表中添加新的Measurement Type,则可以右键单击并运行T4-Template,并为表中添加的新行生成枚举代码。 我不喜欢我的应用程序中没有链接到我的数据库的硬编码数据,因此提到了T4模板方法。 它是不可扩展的,否则……如果某个其他外部系统想要检索我们系统中使用的测量标准,那么它在系统中是硬编码的,您不能通过服务将其暴露给客户端。 那就离开了。

  2. 如果目的不是数据相关的,并且你有一些逻辑分配给特定的枚举,那么NO! 这违反了SOLID(开放式关闭原则),就像你在应用程序的某个地方应用一个开关或一堆Ifs来操作每个枚举的逻辑一样,如果你这样做真的很糟糕这些开关或者Ifs都在整个节目……祝你好运添加一个新的枚举……所以它不是为了扩展而打开,而是因为你需要修改现有代码而根据SOLID原则关闭修改。

    如果您的选择是2,那么我建议您使用@danijels评论中的示例用以下内容替换您的枚举:

     public interface IMeasurementUnitType { int ID { get; } string Description { get; } // Just added to simulate a action needed in the system string GetPrintMessage(int size); } 

上面的代码定义了每个度量应遵循的接口(代码契约)。 现在让我们定义百分比和像素测量:

  public class PixelsMeasurementUnitType : IMeasurementUnitType { public int ID => 1; public string Description => "Pixel"; public string GetPrintMessage(int size) { return $"This is a {Description} Measurement that is equal to {size} pixels of the total screen size"; } } public class PercentMeasurementUnitType : IMeasurementUnitType { public int ID => 2; public string Description => "Persentage"; public string GetPrintMessage(int size) { return $"This is a {Description} Measurement that is equal to {size} persent of total screen size (100)"; } } 

所以我们定义了两种类型,我们将在代码中使用它们,如下所示:

  var listOfMeasurmentTypes = AppDomain.CurrentDomain.GetAssemblies() .SelectMany(s => s.GetTypes()) .Where(p => typeof(IMeasurementUnitType).IsAssignableFrom(p) && !p.IsInterface) .ToList(); 

这里我们获取扩展IMeasurementUnitType接口的所有TYPES而不是接口本身。 现在我们可以使用Activator创建类的实例来填充我们的UI控件:

  public IEnumerable GetInstantiatedClassesFromTypes(List types) { foreach (var type in types) { yield return (IMeasurementUnitType)Activator.CreateInstance(type); } } 

您可以将上面的代码更改为任何类型的通用代码,现在生活发生,客户端提供一个名为Point的新测量单位类型作为新要求,我不需要更改任何代码,只需添加新类型(扩展)代码不修改)。 新类型将在应用程序中自动获取。

  public class PointMeasurementUnitType : IMeasurementUnitType { public int ID => 3; public string Description => "Point"; public string GetPrintMessage(int size) { return $"This is a {Description} Measurement that is equal to {size} points of total screen size"; } } 

一个好主意是在启动应用程序时尝试缓存类型以获得性能优势,或者尝试使用您选择的DI容器。

此外,有人可以争辩说,在你的应用程序的某个地方,你需要区分类型,我同意,但是你想要苹果与苹果。 因此,尽可能尝试应用与此类型相同的原则。 如果在某种类型的图形处理器(例如)类中使用此类型,则具有IGraphicsProcessor并具有区分这些类型的具体类,例如PersentageAndPixelGraphicsProcessor(从IGraphicsProcessor扩展),或者如果它仅区分一种类型,则称为PersentageGraphicsProcessor。

很抱歉HUGE SA,但我真的很喜欢enum,但是当我尝试使用枚举分离逻辑时,我觉得它是一个强大的反模式。

欢迎评论,