使用和坚持枚举的最佳实践
我在这里看到了几个关于处理和坚持类枚举值的最好方法的问题/讨论(例如持久化数据适合于枚举 , 如何使用NHibernate持久化枚举 ),以及我想问一下这个概念是什么。
尤其是:
- 代码中应该如何处理这些值?
- 他们应该如何坚持到数据库(作为文本/数字)?
- 什么是不同的解决scheme的权衡?
注意:我把原来包含在这个问题中的解释移到了答案上。
我很同意你说的话。 有一件事我想追加一下枚举的持久性:我不相信从DB值生成枚举的生成是可以接受的,但是我也认为运行时检查并不是一个好的解决scheme。 我将定义第三种方法:有一个unit testing,将检查枚举的值与数据库。 这样可以防止“随意”的分歧,并避免每次运行代码时检查枚举数据库的开销。
最初的文章看起来很好。 不过,基于评论,似乎有关Java枚举的一些评论可能会澄清一些事情。
Java中的枚举types是一个定义类,但许多程序员往往会忘记这一点,因为它们将它与其他语言中的“允许值列表”联系起来。 不仅如此。
所以,为了避免这些switch语句,在enum类中添加一些代码和其他方法可能是合理的。 几乎没有必要创build一个单独的“枚举类真实类”。
还要考虑文档的要点 – 你想logging在数据库中的枚举的实际含义? 在源代码中反映值(你的枚举types)还是在一些外部文档中? 我个人比较喜欢源代码。
如果由于速度或其他原因想要将枚举值作为整数呈现在数据库中,则该映射也应驻留在Java枚举中。 你会得到默认的string名称映射,我已经满足于此。 有一个与每个枚举值相关的序号,但直接用它作为代码和数据库之间的映射不是很明亮,因为如果有人对源代码中的值进行重新sorting,则序号会改变。 或者在现有值之间添加额外的枚举值。 或者删除一些价值。
(当然,如果有人在源代码中改变了枚举的名字,默认的string映射也会变得很糟糕,但是这种情况不太可能发生,如果需要的话,可以通过一些运行时检查并按照这里的build议检查数据库中的约束。
Java或C#应该总是在代码中使用枚举。 免责声明:我的背景是C#。
如果要将值保存到数据库中,则应明确定义每个枚举成员的整数值,以便以后的代码更改不会意外更改已转换的枚举值以及应用程序行为。
应始终将值作为整数值持久保存到数据库,以防止枚举名称重构。 保持维基上的每个枚举的文档,并向数据库字段添加注释,指向维基页面loggingtypes。 还要将XML文档添加到包含Wiki条目链接的枚举types,以便通过Intellisense可用。
如果使用工具来生成CRUD代码,它应该能够定义一个枚举types用于列,以便生成的代码对象总是使用枚举成员。
如果自定义逻辑需要应用于枚举成员,则可以使用以下选项:
- 如果你有一个枚举MyEnum,创build一个静态类MyEnumInfo,它提供了实用的方法来发现关于枚举成员的附加信息,通过switch语句或者任何必要的手段。 将“Info”附加到类名称中的枚举名称末尾,确保它们在IntelliSense中彼此相邻。
- 装饰具有属性的枚举成员来指定其他参数。 例如,我们开发了一个EnumDropDown控件,该控件创build了一个填充了枚举值的ASP.NET下拉列表,并且EnumDisplayAttribute指定了用于每个成员的格式良好的显示文本。
我还没有尝试过,但在SQL Server 2005或更高版本中,理论上可以在数据库中注册包含枚举信息的数据库,并将值转换为枚举以在视图或其他构造中使用,从而生成一种转换数据以一种更易于DBA使用的方式进行。
在C#的代码处理中,你已经错过了定义delcaring的0值。 我几乎没有失败总是声明我的第一个值为:
public enum SomeEnum { None = 0, }
作为一个空值。 由于支持types是一个整数,整数默认为0,所以在很多地方知道一个枚举是否已经被编程设置。
由于需要额外的空间和较慢的search,将枚举的文本值存储在数据库中对于存储整数来说是不太优选的。 它的意义比数字更有价值,但是数据库是用于存储的,而表示层则是为了让事情看起来很好。
那么,根据我的经验,使用枚举除了将选项(作为标志)传递给即时方法调用之外,其他方面都会导致switch
。
- 如果你打算在你的代码中使用枚举,那么你可能会得到不易维护的代码(臭名昭着的
switch
语句) - 扩展枚举是一个痛苦。 你添加一个新的枚举项,并最终通过所有的代码来检查所有条件。
- 在.NET 3.5中,您可以将扩展方法添加到枚举中,以使它们更像类。 然而,以这种方式添加真正的function并不是那么容易,因为它还不是一个类(如果不是其他地方,最终会在扩展方法中使用
switch
-es。
因此,对于一个多function的类枚举实体,您应该花一些时间并将其作为一个类来创build,并记住以下几点:
- 为了让你的类像一个枚举行为,你可以强制每个派生类实例化为一个单例,或者覆盖等于允许不同实例的值比较。
- 如果你的类是枚举类的,它应该表示它不应该包含可序列化的状态 – 反序列化应该可以从它的types本身(一种“ID”,如你所说)。
- 持久性逻辑应该仅限于基类,否则扩展你的“枚举”将是一场噩梦。 如果你去了Singleton模式,你需要确保正确的反序列化到单例实例中。
每次你find你的自我使用代码中的“幻数”更改为枚举。 除了节省时间(因为当bug出现的时候魔法就会消失),它会保存你的眼睛和记忆(有意义的枚举使代码更具可读性和自我logging),因为猜测 – 你最有可能是维护和开发的人你自己的代码
我试图总结我的理解。 如果您有任何更正,请随意编辑。 所以在这里:
在代码中
在代码中,枚举应该使用语言的本地枚举types(至less在Java和C#中)或类似“types安全枚举模式”来处理 。 使用普通常量(整型或类似)是不鼓励的,因为你失去了types安全(并且很难理解哪些值是合法input,例如方法)。
这两者之间的select取决于多less额外的function是要附加到枚举:
- 如果你想把大量的function放到枚举中(这是很好的,因为你总是避免使用switch),所以一个类通常更合适。
- 另一方面,对于简单的枚举types的值,语言的枚举通常更清晰。
特别是,至less在Java中,枚举不能从另一个类inheritance,所以如果你有几个想要放进一个超类的具有相似行为的枚举,那么就不能使用Java的枚举。
坚持枚举
要坚持枚举,每个枚举值应该被分配一个唯一的ID。 这可以是一个整数或一个短的string。 一个简短的string是首选,因为它可以是助记符(使DBA等更容易理解数据库中的原始数据)。
- 在软件中,每个枚举应该具有映射函数,以便在枚举(用于软件内部)和ID值(用于持久化)之间进行转换。 一些框架(例如(N)Hibernate)自动执行此操作的支持有限。 否则,你必须把它放入枚举types/类。
- 数据库应该(理想情况下)包含列出合法值的每个枚举的表。 一列是ID(见上),这是PK。 附加的列可能对于例如描述是有意义的。 所有将包含该枚举值的表列可以使用这个“枚举表”作为FK。 这保证了不正确的枚举值永远不会被持久化,并且允许DB“独立”。
这种方法的一个问题是合法枚举值列表存在于两个地方(代码和数据库)。 这很难避免,因此经常被认为是可以接受的,但是有两种select:
- 只保留数据库中的值列表,在构build时生成枚举types。 优雅,但意味着一个数据库连接是需要运行的构build,这似乎有问题。
- 在代码中定义值为权威的列表。 在运行时(通常在启动时)检查数据库中的值,在不匹配时发生抱怨/中止。
Imho,至于代码部分:
你应该总是使用enumtypes来枚举,基本上你可以得到很多免费赠品:types安全性,封装和切换回避,一些集合的支持,如EnumSet
和EnumMap
和代码清晰度。
至于持久性部分,您始终可以使用enum.valueOf(String)方法持久化枚举的string表示forms并将其加载回去。
我知道这是一个旧的论坛,如果数据库可能有其他东西直接整合到它呢? 例如,当生成的DB是代码的唯一目的时。 那么,你将在每个整合中定义枚举。 最好让他们在数据库中。 否则,我同意原来的post。