Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/335.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 通过枚举属性而不是使用GetType()来标识对象类型是否是一种糟糕的做法?_C#_Types_Interface_Enums - Fatal编程技术网

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
的每个对象是否都有自定义属性?我的应用程序已经运行了一系列“自测试”(更侧重于数据库配置,但没有理由不检查)。是的,尽管您可能想启动一个单元测试项目来捕获这一点