我有一组对象都实现了一个(自定义)接口:IAuditEvent
。
每个对象都可以存储在数据库中,并且每个对象类型都使用唯一的数字ID。
存储对象的方法循环在List<IAuditEvent>
周围,因此需要知道每个对象的特定类型才能存储正确的数字ID。
在IAuditEvent
上设置枚举属性是不是很糟糕,以便每个对象都可以使用唯一的枚举值来识别其类型?
我可以看到最简单的解决方案是编写一个将Type
转换为整数的方法,但是如果我需要为另一个目的枚举审计事件呢?在IAuditEvent
上有我的枚举属性是否仍然是错误的?
答案 0 :(得分:2)
此数据库类型ID(或鉴别符)本质上是每种类型的元数据。在每个实例上混合数据和元数据并不是很好。我首选的解决方案是编写自定义属性来保存此元数据,将其应用于每种类型,然后使用GetCustomAttributes
Type
方法读取这些元数据。
[DatabaseDiscriminator(123)]
public class MyAuditEvent : IAuditEvent
{
}
答案 1 :(得分:1)
是的,这很糟糕。您现在假设IAudit
的每个实现都知道其他实现,因为它们都应该具有唯一ID;此外,您需要为每个新接口实例的enum
添加一个新值。这只是应用程序内部不需要的额外信息,只是在数据表示中。
而是在业务层中有一个查找表:
new Dictionary<Type, int> {
{ typeof(UserAudit), 1 },
{ typeof(OrderAudit), 2 }
}
答案 2 :(得分:1)
简短回答:这取决于。
记住接口的用途。它们的全部意义是将实现隐藏到接口的用户。说到接口,我看到两种类型的代码:
使用接口的代码。这段代码应该只知道IAuditEvent,而不是它的实现类。如果这段代码需要知道不同类型的审计事件(我的意思是“一般意义上的”类型“,而不是具体的类),那么我会说将一个Type属性添加到IAuditEvent是一个好习惯。就用户而言,甚至不需要为每种类型提供不同的实现。
另一种类型的代码是实现接口的代码,我的意思不仅仅是从IAuditEvent继承的类,还包括构造并且意味着直接使用这些实现的类。如果这个只有这个代码需要知道它正在处理什么类型的IAuditEvent(这里我的意思是类在类中),那么我会说添加一个Type属性是不好的做法,因为它暴露了实现的位。此代码也可以执行检查实例。
答案 3 :(得分:0)
实现接口的目的是抽象出实现 - 如果你使用接口而不关心实现类型,因此不需要用枚举值来识别它。
话虽如此,我这样做的方法是拥有一个公共基类型,它既实现了接口,又有一个返回枚举的抽象属性:
public abstract class BaseType : IAuditEvent
{
public abstract MyTypeEnum TypeId { get; }
... add any base implementation of the interface ...
}
然后在每个派生对象中:
public class MyConcreteType : BaseType
{
public MyTypeEnum TypeId { get { return MyTypeEnum.SpecificValue; } }
... any overrides, etc ....
}
这种方法有几个优点:
它可以保持您的代码清洁。在许多类中实现接口时,很可能会有一些不同对象可以共享的接口的通用实现,这可以放在基类中。明智地使用abstract
和virtual
方法/属性。
使用枚举来识别对象可以帮助避免在基于实现者的类型进行分支时无休止且繁琐的if (myObj.GetType() == typeof(ObjectA)) {} else if (myObject.GetType() == typeof(ObjectB))...
语句 - 现在您可以使用基于的switch语句TypeId属性返回的枚举
如果你添加更多的实现,你仍然会留下必须扩展枚举的问题,但这是一个相对简单的代码更改,如果你要添加更多的实现,你必须重写(所以扩展enum isn不是很重要,但如果可能的话,你确实希望避免更改已经分配的值。