簡體   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