C# 通过枚举属性而不是使用GetType()来标识对象类型是否是一种糟糕的做法?
我有一组对象,它们都实现了一个(自定义)接口:C# 通过枚举属性而不是使用GetType()来标识对象类型是否是一种糟糕的做法?,c#,types,interface,enums,C#,Types,Interface,Enums,我有一组对象,它们都实现了一个(自定义)接口:IAuditEvent 每个对象都可以存储在数据库中,并且每个对象类型都使用一个唯一的数字id 存储对象的方法在列表中循环,因此它需要知道每个对象的特定类型,以便存储正确的数字id 在IAuditEvent上具有枚举属性,以便每个对象都可以用唯一的枚举值标识其类型,这是一种不好的做法吗? 我可以看出,最简单的解决方案是编写一个方法,将类型转换为整数,但如果出于其他目的需要审计事件的枚举,该怎么办?将我的枚举属性放在IAuditEvent上是否仍然是错
IAuditEvent
每个对象都可以存储在数据库中,并且每个对象类型都使用一个唯一的数字id
存储对象的方法在列表中循环,因此它需要知道每个对象的特定类型,以便存储正确的数字id
在IAuditEvent
上具有枚举属性,以便每个对象都可以用唯一的枚举值标识其类型,这是一种不好的做法吗?
我可以看出,最简单的解决方案是编写一个方法,将类型
转换为整数,但如果出于其他目的需要审计事件的枚举,该怎么办?将我的枚举属性放在IAuditEvent
上是否仍然是错误的?是的,这很糟糕。现在假设IAudit
的每个实现都知道其他实现,因为它们都应该有一个唯一的ID;此外,您需要为接口的每个新实例向enum
添加一个新值。这只是应用程序内部不需要的额外信息,而只是数据表示中需要的信息
而是在业务层中创建一个查找表:
new Dictionary<Type, int> {
{ typeof(UserAudit), 1 },
{ typeof(OrderAudit), 2 }
}
新字典{
{typeof(UserAudit),1},
{typeof(OrderAudit),2}
}
此数据库类型id(或鉴别器)本质上是每种类型的元数据。在每个实例上混合数据和元数据并不好。我的首选解决方案是编写一个自定义属性来保存此元数据,将其应用于每种类型,然后使用type
的GetCustomAttributes
方法读取这些元数据
[DatabaseDiscriminator(123)]
public class MyAuditEvent : IAuditEvent
{
}
简短回答:这要看情况而定
记住接口的用途。它们的全部目的是向界面的用户隐藏实现。谈到接口,我看到两种类型的代码:
使用接口的代码。这段代码应该只知道IAuditeEvent,而不知道它的实现类。如果这段代码需要了解不同类型的审计事件(我指的是最一般意义上的“类型”,而不是具体的类),那么我认为将类型属性添加到IAuditeEvent是一种很好的做法。就用户而言,对于每种类型,甚至不需要有不同的实现
另一种类型的代码是实现接口的代码,我指的不仅仅是从IAuditEvent继承的类,还包括构造并直接用于这些实现的类。如果这段代码需要知道它处理的是哪种类型的IAuditeEvent(这里我指的是类中的类型),那么我认为添加类型属性是一种不好的做法,因为它会暴露一些实现。这段代码也可以执行instanceof check。实现接口的目的是抽象掉实现——如果您使用接口而不关心实现类型,那么就不需要使用枚举值来标识它
话虽如此,我要做的是拥有一个公共基类型,它既实现了接口,又具有一个返回枚举的抽象属性:
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属性返回的枚举的switch语句
如果添加更多的实现,您仍然会面临必须扩展枚举的问题,但这是一个相对简单的代码更改,如果添加更多的实现,您无论如何都必须重新编译(因此扩展枚举不是什么大问题,但如果可能,您确实希望避免更改已分配的值).为什么让IAuditEvent
的成员识别特定类型的事件是一种糟糕的做法?潜在的问题是什么?哈哈!这就是我要问的我想有些人会争辩说,你给了对象两种不同的方法来标识自己,这两种方法可能不一致。你说的“通过枚举属性标识对象”是什么意思?GetType()可以用来标识对象的类型,而不是标识对象本身……谢谢@user1027167。我已经澄清了我的问题。实现接口的目的是抽象掉实现——如果您使用接口并且不关心实现类型,那么应该不需要使用枚举值来标识它。但是,如果开发人员忘记添加自定义属性,会发生什么情况?你能在编译时捕获它吗?不,它必须是一个运行时异常。但我不认为这是一个巨大的缺点。毕竟,开发人员可以实现一个接口,但将适当的方法保留为抛出新的NotImplementedException()
。是的,这是正确的。我是否可以使用反射来检查实现IAuditEvent
的每个对象是否都有自定义属性?我的应用程序已经运行了一系列“自测试”(更侧重于数据库配置,但没有理由不检查)。是的,尽管您可能想启动一个单元测试项目来捕获这一点