如何在C#中创建类似动态枚举的东西?

时间:2009-08-06 21:25:47

标签: c# design-patterns enums

在我正在构建的应用程序中,我列举了帐户状态:

 public enum AccountStatus
 {
      Active = 1,
      Trial = 2,
      Canceled = 3
 }

但是,我需要来自AccountStatus的更多信息,所以我创建了一个具有一些额外有用属性的类:

 public class AccountStatus
 {
      public int Id {get; set;}
      public string Description {get; set;}
      public bool IsActive {get; set;}
      public bool CanReactivate {get; set;}
 }

此类从数据库表中填充,可能如下所示:

 1,  "Active",       True,  True
 2,  "Trial",        True,  True 
 3,  "ExpiredTrial", False, True
 4,  "Expelled",     False, False

当我有一个使用AccountStatus的客户对象时,这非常方便,因为我可以编写如下代码:

 if(customer.Status.CanReactivate) // Show reactivation form

然而,我失去了同样重要的东西。我不能再这样做了:

 if(customer.Status == AccountStatus.Active)  // allow some stuff to happen

如果可能的话,最好的方法是包含一些能让我模仿课堂内枚举的东西。我知道我可以将公共静态字段添加到AccountStatus类,但最终这不起作用,因为如果数据库发生更改,则必须手动更新代码。通过这个,我的意思是:

 public static readonly AccountStatus Active = new AccountStatus(1);
 public static readonly AccountStatus Trial = new AccountStatus(2);
 // etc, etc ...

我想在某个地方可能存在一种模式,我只是不知道它叫什么。

有什么想法吗?

澄清

基于到目前为止的答案,我需要澄清一些事情。

上表是一个简短的例子。在我的实际表中有很多记录,我现在有12个记录。另外,我们可以添加更多或删除一些现有的。这就是我在问题标题中用“动态”的意思。

其次,我给出了一个非常简单的用例,表示我丢失的能力显然混淆了问题。这是另一个真实的例子:

 if(customer.Status == AccountStatus.Trial || customer.Status == AccountStatus.ExpiredTrial)

...试验和ExpiredTrial都不是属性上的布尔值。我也不想添加它们。这将设置一个比我试图避免的更糟糕的先例(这意味着每次我向表中添加一条新记录时我都必须向该类添加一个新属性。)

更新

我选择的答案并没有真正满足我所寻找的,但暗示我正在寻找不必要的东西。在考虑了这个之后,我同意了。虽然添加枚举或静态字段确实复制了一些工作(即,在代码和表格中都有值),但我认为其好处超过了负面因素。

10 个答案:

答案 0 :(得分:4)

但为什么不能将枚举用作该类的属性..?

public enum State
{
    Active = 1,
    Trial = 2,
    Canceled = 3
}

public class AccountStatus
{
    public int Id {get; set;}
    public State State {get; set;}
    public string Description {get; set;}
    public bool IsActive {get; set;}
    public bool CanReactivate {get; set;}
}

然后:

if(customer.Status == AccountStatus.State.Active)  // allow some stuff to happen

答案 1 :(得分:2)

您可以使用字符串进行比较,而不是使用强类型enum

public static readonly AccountStatus Active = new AccountStatus("Active");

或从数据库加载类型:

public static readonly AccountStatus Trial = new AccountStatus( reader["StatusField"] );

然后,您可以进行明确的比较:

if(customer.Status == "Active")

你失去了强大的打字,但这就是动态的意思:-)。您可以将已知的字符串值存储在常量中以获得其中一些。

修改

您当然可以使用相应的整数值来执行此操作,就像您在帖子末尾所暗示的那样。但字符串更容易阅读,在这种情况下使用整数不提供任何类型的打字好处。

答案 2 :(得分:1)

我认为你可以通过使用Flags枚举来实现这一点,你可以在其中组合值:

[Flags]
public enum AccountStatus
{
    Expelled = 1,
    Active = 2,
    CanReactivate = 4,
    Canceled = 8,
    Trial = Active | CanReactivate,
    ExpiredTrial = CanReactivate,        
}

然而,感觉好像那些不同的枚举值沿着不同的尺度移动(一些描述状态,一些描述有效的动作),所以它可能不是正确的解决方案。也许你应该把它分成两个枚举。

答案 3 :(得分:1)

我不明白为什么你不能写:

if (customer.Status.IsActive)

答案 4 :(得分:1)

如果您在应用程序中执行/想要这样的事情:

  

if(customer.Status ==   AccountStatus.Active)

您必须在代码中知道“有效”是一种可能的状态。你怎么能在你的代码中写出实际的单词Active。状态对象可以是动态的,但使用该状态的程序的其余部分必须知道存在哪些类型的状态才能对其执行有用的操作。如果不再存在活动状态,则状态对象可能不需要重新实现,但使用它的代码会起作用。

如果每种类型的状态都完全由参数完全定义(似乎活动和跟踪具有相同的参数,那么需要更多区分(到期日期?)),然后检查这些参数。

如果参数组合必须具有名称,则可以进行某种查找,您可以将名称转换为其关联参数或其反转。这样,该代码的名称可以是动态的,参数值可达到某种程度。

一个可能的真正动态解决方案是实现某种脚本语言/ xml文件/ ...,使用户能够指定状态类型,它们的参数,并将它们与系统行为相关联。

答案 5 :(得分:1)

我仍然认为你最好的办法就是将缺少的案例添加到课堂上。

 public class AccountStatus
 {
      public int Id {get; set;}
      public string Description {get; set;}
      public bool IsActive {get; set;}
      public bool CanReactivate {get; set;}
      public bool Trial {get; set;}
      public bool ExpiredTrial {get; set;}
 }

你可以用比你的例子更简单的形式打电话:

if(customer.AccountStatus.Trial || customer.AccountStatus.ExpiredTrial)

如果您需要检查UserDefined状态,请将其公开为单独的属性:

public AccountStatusCode Status  {get; set;}

......并且这样称呼它:

if(customer.Status == AccountStatus.Active)

如果要设置初始状态,仍可以向其添加构造函数。

答案 6 :(得分:0)

您可以在AccountStatus和Description之间建立多对多关系。这样,您可以在运行时加载所有不同的描述,然后使用某种枚举进行比较:)

答案 7 :(得分:0)

此代码执行您在帖子中描述的内容。我没有编写CanReactivate代码,因为你没有说出它的逻辑是什么。

 public enum AccountStatusCode
 {
      Active = 1,
      Trial = 2,
      Canceled = 3
 }

 public class AccountStatus
 {
      private AccountStatusEnum status
      //
      // Constructor sets initial status.
      public AccountStatus(int status)
      {
          this.Status = (AccountStatusCode)status;
      }

      public int Id { get; set; }
      public string Description { get; set; }
      //
      // 
      public bool IsActive 
      { 
           get { return status == AccountStatusCode.Active; }
      }
      public bool CanReactivate { get; set; }
 }

请注意,既然您说要将初始帐户状态指定为int,我在构造函数中接受一个int,但之后我将其转换为AccountStatusEnum以将其分配给成员变量。这可能不是最佳实践......您应该将构造函数传递给AccountStatusCode值。

答案 8 :(得分:0)

好的,如果你在C#3.0工作,你可以试试extension methods

// Define extension method like this:
public static bool IsActive(this AccountStatus status)      
{            
    get { return status == AccountStatusCode.Active; }      
}
// 
// Call it like this:
if (AccountStatus.IsActive())

这使它远离你的班级。

答案 9 :(得分:0)

我认为柔道试图解释的是 - 数据库中的新状态需要在条件块中为新状态进行检查。我想我也在做同样的事情。我唯一要做的就是我也在使用另一个'rank'字段,以便我可以进行范围比较而不是硬编码所有状态。 例如,而不是:

if(customer.Status == AccountStatus.Trial || customer.Status == AccountStatus.ExpiredTrial)

如果我可以把它们按顺序排列:

if(customer.Status< AccountStatus.Trial)在我们的枚举中我们可以按顺序放置它们。因此,新页面中的新状态不会破坏其他页面的逻辑(取决于状态的等级)。