如何在 C# 中制作动态枚举之类的东西?

发布于 2024-07-30 13:50:13 字数 1847 浏览 3 评论 0原文

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

 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 都不是属性上的布尔值。 我也不想添加它们。 这将开创一个比我试图避免的先例更糟糕的先例(这意味着每次我向表中添加新记录时,我都必须向类添加一个新属性)。

更新

我选择的答案并不真正符合我正在寻找的答案,但表明我正在寻找不必要的东西。 考虑到这一点后,我同意了。 虽然添加枚举或静态字段确实会重复一些工作(即在代码和表中都有值),但我认为好处大于坏处。

In an application I'm building I had an enumeration of account statuses:

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

However, I needed more information from an AccountStatus so I made a class which has a few extra useful properties:

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

This class get populated from a database table that might look like this:

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

This is really handy when I have a customer object that uses the AccountStatus because I can write code like:

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

However, I have lost something equally important. I can no longer do this:

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

What would be the best way, if its even possible, to include something that will allow me to mimic the enumeration within the class. I know that I could add public static fields to the AccountStatus class, but ultimately this doesn't work because if the database changes the code would have to be manually updated. By this, I mean:

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

I imagine there is probably a pattern for this somewhere, I just don't know what its called.

Any ideas?

CLARIFICATION

Based on the answers so far I need to clarify a couple of things.

The table above is a brief example. In my actual table there a many records, I have 12 in there right now. Plus we can add more or remove some existing. This is what I meant by "dynamic" in my question title.

Secondly, I gave a very simple use case for the ability I lost which apparently confused matters. Here is another real example:

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

... neither Trial nor ExpiredTrial are boolean values on the property. I don't want to add them either. That would set an even worse precedent than the one I'm trying to avoid (meaning I would have to add a new property to the class every time I added a new record to the table).

UPDATE

I selected an answer which didn't really meet was I was looking for, but suggests that I was looking for something unnecessary. After thinking about this, I concur. While adding an enum or static fields does duplicate some work (ie, having the values in both code and in a table) I think the benefits outweigh the negatives.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

泪意 2024-08-06 13:50:14

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

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

如果我可以将它们按顺序排列,我可以这样做:

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

I think what judo tried to explain is - a new status in the DB will require to put checking s for that new status in the conditional blocks. I think I am also doing something same. Only thing I am doing is I am also using another 'rank' field so that I can do range comparison instead of hard coding all the statuses.
For example instead of doing:

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

if I could put them in order I could do:

if(customer.Status < AccountStatus.Trial) as in our enum we can put them as ordered. So a new status in a new page wont break the other page's logics (depends on the rank of the status).

青丝拂面 2024-08-06 13:50:13

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

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

But why can't you use the enumeration as a property of that class..?

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;}
}

And then:

if(customer.Status == AccountStatus.State.Active)  // allow some stuff to happen
韶华倾负 2024-08-06 13:50:13

您可以只使用字符串进行比较,而不是使用强类型枚举:

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

或者从数据库加载类型:

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

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

if(customer.Status == "Active")

您会失去强类型,但这就是动态意味着:-)。 您可以将已知的字符串值存储在常量中以获取其中的一些值。

编辑

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

Rather than working with a strongly-typed enum, you could just do comparisons using a string:

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

or load the type from your database:

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

You can then do explicit comparisons:

if(customer.Status == "Active")

You lose the strong typing, but that's what dynamic means :-). You can store the known string values in constants to get some of this back.

edit

You could of course do this using the corresponding integer values, like you hinted at the end of your post. But strings are easier to read, and in this case using integers doesn't offer any sort of typing benefits.

似狗非友 2024-08-06 13:50:13

我认为您可以通过使用可以组合值的 Flags 枚举来实现此目的:

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

但是,感觉好像这些不同的枚举值沿着不同的比例移动(有些描述状态,有些描述有效操作),所以这可能不是正确的解决方案。 也许您应该将其分成两个枚举。

I think you could achieve this by using a Flags enumeration where you can combine values:

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

However, it feels as if those different enum values move along different scales (some describe state, some describe valid actions), so it might not be the right solution. Perhaps you should instead split it into two enums.

十级心震 2024-08-06 13:50:13

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

if (customer.Status.IsActive)

I don't understand why you can't just write:

if (customer.Status.IsActive)
鯉魚旗 2024-08-06 13:50:13

如果您在应用程序中确实/想要这样的东西:

if(客户.状态==
帐户状态.Active)

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

如果每种状态都像看起来一样完全由参数定义(活动和跟踪具有相同的参数,因此需要更多的参数来区分(到期日期?)),然后检查这些参数。

如果参数组合必须有一个名称,则创建某种可查找的名称,您可以将名称转换为其关联的参数或其反参数。 这样,该代码段的名称可以是动态的,并且参数值在某种程度上是动态的。

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

if you do/want something like this in your application:

if(customer.Status ==
AccountStatus.Active)

You have to know in your code that "Active" is a possible status. How else would you be able to write the actual word Active in your code. The status object can be dynamic, but the rest of the program that uses the status has to know what types of status exist in order to do something useful with it. What if active doesn't exist anymore, the status object may not need to be reimplemented, but the code that uses it does.

If every kind status is fully defined by parameters like it almost seems (active and trail have the same parameters, so more are needed to differentiate (expiration date?)), then check those parameters.

If a combination of parameters has to have a name, then make some kind of lookuptable where you can translate the name into its associated parameters, or its inverse. That way the name can be dynamic for that piece of code, and the parameter values are to upto some degree.

A possible real dynamic solution would be to implement some sort of scripting language/xml file/... that enables the user to specify the kinds of status, their parameters, and associate them with system behavior.

画骨成沙 2024-08-06 13:50:13

我仍然认为你最好的选择是将你丢失的案例添加到班级中。

 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)

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

I still think your best bet is to add your missing cases to the class.

 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;}
 }

Which you can call in a simpler form than your example:

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

If you need to check a UserDefined status, expose that as a separate property:

public AccountStatusCode Status  {get; set;}

...and call it like this:

if(customer.Status == AccountStatus.Active)

You can still add a constructor to it if you want to set an initial status.

魂归处 2024-08-06 13:50:13

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

You could make a many-to-many relationship between AccountStatus and Description. This way you can, at runtime, load all the different Descriptions you got, and then compare against those, using some sort of enumeration :)

过期以后 2024-08-06 13:50:13

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

This code does what you described in your post. I didn't code CanReactivate because you didn't say what the logic for that was.

 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; }
 }

Note that, since you said you wanted to specify the initial account status as an int, I accept an int in the constructor, but then I cast it to an AccountStatusEnum for assigning it to the member variable. That's probably not the best practice...You should pass the constructor an AccountStatusCode value.

静若繁花 2024-08-06 13:50:13

好的,如果您使用 C# 3.0,您可以尝试 扩展方法< /a>:

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

这使它远离你的班级。

OK, well if you're working in C# 3.0, you could try 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())

That keeps it out of your class.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文