通过枚举属性而不是使用 GetType() 来识别对象类型是一种不好的做法吗?
我有一组对象,它们都实现一个(自定义)接口:IAuditEvent
。
每个对象都可以存储在数据库中,并且每个对象类型都使用唯一的数字 ID。
存储对象的方法围绕 List
循环,因此它需要知道每个对象的具体类型才能存储正确的数字 id。
在 IAuditEvent
上拥有枚举属性以便每个对象都可以使用唯一的枚举值来标识其类型,这种做法是否不好?
我可以看到最简单的解决方案是编写将 Type
转换为整数的方法,但是如果我需要出于其他目的枚举审核事件怎么办?将我的枚举属性放在 IAuditEvent
上是否仍然是错误的?
I have a collection of objects that all implement one (custom) interface: IAuditEvent
.
Each object can be stored in a database and a unique numeric id is used for each object type.
The method that stores the objects loops around a List<IAuditEvent>
, so it needs to know the specific type of each object in order to store the correct numeric id.
Is it poor practice to have an enumeration property on IAuditEvent
so that each object can identify its type with a unique enumeration value?
I can see that the simplest solution would be to write a method that translates a Type
into an integer, but what if I need an enumeration of audit events for another purpose? Would it still be wrong to have my enumeration property on IAuditEvent
?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
该数据库类型 ID(或鉴别器)本质上是每种类型的元数据。在每个实例上混合数据和元数据并不是很好。我的首选解决方案是编写一个自定义属性来保存此元数据,将其应用于每种类型,并使用
Type
的GetCustomAttributes
方法读取这些元数据。This database type id (or discriminator) is essentially metadata of each type. Mixing data and metadata on each instance isn't great. My preferred solution would be to write a custom attribute to hold this metadata, apply it to each type, and read these using the
GetCustomAttributes
method ofType
.是的,这很糟糕。您现在假设 IAudit 的每个实现都知道其他实现,因为它们都应该有一个唯一的 ID;此外,您需要为接口的每个新实例添加一个新值到
enum
。这只是应用程序内部不需要的额外信息,仅在数据表示中需要。而是在您的业务层中有一个查找表:
Yes it's bad. You are now assuming that every implementation of
IAudit
knows about the other implementations, because they should all have a unique ID; furthermore you need to add a new value to theenum
for every new instance of the interface. This is just extra information that isn't needed inside the application but just in the data representation.Rather have a lookup table in your business layer:
简短的回答:这取决于。
记住接口的用途。它们的全部目的是向界面用户隐藏实现。当谈到接口时,我看到两种类型的代码:
使用接口的代码。这段代码应该只知道 IAuditEvent,而不是它的实现类。如果此代码需要了解不同类型的审核事件(我的意思是最一般意义上的“类型”,而不是具体的类),那么我认为向 IAuditEvent 添加 Type 属性是一个很好的做法。就用户而言,甚至不需要为每种类型提供不同的实现。
另一种类型的代码是实现接口的代码,我的意思不仅是从 IAuditEvent 继承的类,还包括构造并直接使用这些实现的类。如果这个并且只有这个代码需要知道它正在处理什么类型的IAuditEvent(这里我指的是类中的类型),那么我会说添加 Type 属性是不好的做法,因为它暴露了实现的一些部分。这段代码也可以做一个instanceof 检查。
Short answer: it depends.
Remember what interfaces are for. The entire point of them is to hide the implementation to the users of the interface. When it comes to interfaces i see two types of code:
Code that uses the interface. This code should only know about IAuditEvent, and not its implementing classes. If this code needs to know about different types of audit events (i mean "type" in the most general sense, not classes specifically), then i'd say it's good practice to add a Type property to IAuditEvent. As far as the user is concerned, there does not even need to be a different implementation for each type.
The other type of code is code that implements the interface, and i mean not just classes that inherit from IAuditEvent but also classes that construct and are meant to work with these implementations directly. If this and only this code needs to know what type of IAuditEvent it is dealing with (and here i mean type as in class), then i'd say it is bad practice to add a Type property, since it exposes bits of the implementation. This code could just as well do a instanceof check.
实现接口的目的是抽象实现 - 只要您使用接口并且不关心实现类型,因此不需要用枚举值来标识它。
话虽如此,我要做的方法是拥有一个公共基类型,它既实现接口又具有返回枚举的抽象属性:
然后在每个派生对象中:
这种方法有几个优点
使用枚举来标识对象可以帮助避免那些无休止且乏味的
if (myObj.GetType() == typeof(ObjectA)) {} else if (myObject.GetType() == typeof(ObjectB) )...
语句,当需要根据实现者的类型进行分支时 - 现在您只需使用基于 TypeId 属性返回的枚举的 switch 语句,
如果您添加更多实现,您仍然会遇到必须扩展枚举的问题,但那就是一个相对简单的代码更改,并且如果要添加更多实现,则无论如何都必须重新编译(因此扩展枚举并不是什么大问题,但如果可能的话,您确实希望避免更改已分配的值)。
The purpose of implementing an interface is to abstract away the implementation - IOW you use the interface and don't care about the implementing type, therefore there should be no need to identify it with an enum value.
Having said that, the way I would do it is to have a common base type which both implements the interface and has an abstract property that returns the enum:
then in each deriving object:
This approach has a couple of advantages:
it keeps your code clean. When implementing the interface in a number of classes there is a good chance that there will be some common implementation of the interface that the different objects can share, that can go in the base class. Make judicious use of
abstract
andvirtual
methods/properties.using an enum to identify your objects can help avoid those endless and tedious
if (myObj.GetType() == typeof(ObjectA)) {} else if (myObject.GetType() == typeof(ObjectB))...
statements when it comes time to branch based on the type of the implementor - now you can just use a switch statement based on the enum returned by the TypeId propertyYou will still be left with the issue of having to extend the enum should you add more implementations, but that is a relatively simple code change, and you have to recomplie anyway if you are adding more implementations (so extending the enum isn't a big deal, but you do want to avoid changing the already assigned values if possible).