繁体   English   中英

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

[英]How can I make something like a dynamic enumeration in C#?

在我正在构建的应用程序中,我有一个帐户状态的枚举:

 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)

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

UPDATE

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

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

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

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

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

或者从数据库中加载类型:

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

然后,您可以进行显式比较:

if(customer.Status == "Active")

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

编辑

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

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

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

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

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

if (customer.Status.IsActive)

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

if(customer.Status == AccountStatus.Active)

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

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

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

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

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

 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)

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

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

此代码执行您在帖子中描述的内容。 我没有编写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值。

好吧,如果你在C#3.0工作,你可以尝试扩展方法

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

这使它远离你的课堂。

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

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

如果我能把它们整理好,我可以这样做:

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM