2012-05-22 37 views
5

我正在寫一些代碼,並使用基於枚舉值的簡單switch語句。我想到在將來某個時候開發人員可能會添加一個新值,所以我在運行時包含了一個默認方法來捕獲這個值,並拋出一個異常。然而,我意識到每次放入這樣的邏輯時我都應該這樣做,並且我只會在運行時看到這些問題,而不是編譯時間。
我想知道是否有一些代碼可以讓編譯器告訴開發者在更新枚舉值時需要更新某些方法 - 而不僅僅是向枚舉本身添加註釋?C#確保有效的枚舉值 - 面向未來的方法

例如(下面的例子純粹是理論上的;我從開發生命週期中選擇狀態以確保它對大多數人來說都是熟悉的)。

public enum DevelopmentStatusEnum 
{ 
    Development 
    //, QA //this may be added at some point in the future (or any other status could be) 
    , SIT 
    , UAT 
    , Production 
} 

    public class Example 
    { 
     public void ExampleMethod(DevelopmentStatusEnum status) 
     { 
      switch (status) 
      { 
       case DevelopmentStatusEnum.Development: DoSomething(); break; 
       case DevelopmentStatusEnum.SIT: DoSomething(); break; 
       case DevelopmentStatusEnum.UAT: DoSomething(); break; 
       case DevelopmentStatusEnum.Production: DoSomething(); break; 
       default: throw new StupidProgrammerException(); //I'd like the compiler to ensure that this line never runs, even if a programmer edits the values available to the enum, alerting the program to add a new case statement for the new enum value 
      } 
     } 
     public void DoSomething() { } 
    } 
    public class StupidProgrammerException: InvalidOperationException { } 

這有點學術性,但我可以看到它有助於使我的應用程序健壯。有沒有人曾經嘗試過/有什麼好的想法可以實現?

由於提前,

JB

+2

我這樣做就像你做的一樣。 –

+2

每當你發現自己寫了一個switch語句,你應該考慮將代碼重構爲使用多態。 Martin Fowler的着作[Refactoring](http://martinfowler.com/books/refactoring.html)[見這個exerpt](http://sourcemaking.com/refactoring/replace-conditional-with-polymorphism)。這將使您的代碼遵守開放原則(OCP),並幫助下一位開發人員避免錯過更改某處的switch語句。 :-) –

回答

11

在這種情況下,我會盡量不使用枚舉,而是使用類的實例的公共靜態只讀字段。例如,看看.NET框架用顏色做了什麼。有一個Color類,你可以使用Color.Black,Color.Blue等對象。它們不是常量,但提供幾乎所有相同的好處。另外他們還有常數沒有的其他好處。請參閱C#版本3語言規範,其中也談到了這一點。

但是,這個想法是,你沒有一個案例陳述。您可以爲每個「枚舉」成員添加足夠的其他屬性,以使方法(DoSomething或其他)可以正確處理它。當另一個開發人員想要添加另一個成員對象時,他需要提供所需的屬性。我的例子:我需要一個「枚舉」用戶可以在系統中採取的不同動作。這些操作需要檢查權限,記錄等。我還需要父母和孩子的操作(重命名某些內容是「編輯它的一部分」等),用於將操作分組在一起以進行過濾的抽象操作以及特殊操作「全部「和」無「(無定義)。每個人都需要數據庫中的ID和文本。如果有人發明了一種新的動作,我希望它仍然可以工作。我所做的就是這樣的事情(大量的代碼只是爲了給你這個想法而省略):

public class Action 
    { 
    protected Action(bool Abstract, Action Parent, int ID, string Name, bool Undefined) 
    { /* snip */ } 
    protected Action(bool Abstract, Action Parent, int ID, string Name) 
     : this(Abstract, Parent, ID, Name, false) 
    { } 
    //---------------------------------------------------------------------------------------- 
    public static readonly Action All = new Action(true, null, 0, "All"); 
    public static readonly Action None = new Action(false, All, 6, "(Undefined)", true); 
    public static readonly Action Modifying = new Action(true, All, 1, "Modifying"); 
    public static readonly Action Creating = new Action(false, Modifying, 2, "Creating"); 
    public static readonly Action Deleting = new Action(false, Modifying, 3, "Deleting"); 
    public static readonly Action Editing = new Action(false, Modifying, 4, "Editing"); 
    public static readonly Action Exporting = new Action(false, All, 5, "Exporting"); 
    public static readonly Action Renaming = new Action(false, Editing, 7, "Renaming"); 
    /* snip */ 
    //---------------------------------------------------------------------------------------- 
    /* template for new entries: 
    public static readonly Action = new Action(false, All, , ""); 
    */ 
    } 

還有更多的動作。在其他類中有許多操作方法。只要每個行動都提供所需的信息,他們都會繼續工作。添加動作的開發人員被迫提供信息。如果當前屬性對於未來某些「特殊」操作不夠用,那麼稍後必須添加更多屬性。請注意,構造函數是受保護的,所以只有類本身才能創建動作。我在主構造函數中省略了很多代碼,用於檢查重複的ID和名稱以及其他許多事情。您現在可以像這樣使用它:

Log.LogAction(Action.Renaming); 

方法LogAction在其中沒有case語句。它使用操作的屬性。

你的枚舉是什麼?

問候

+0

我喜歡它 - 不確定它是否適用於所有用例,但絕對有用。可悲的是我不記得我的原始使用案例是什麼促使我想到這個問題 - 這是我多次需要枚舉時的一個問題。現在我知道了這種方法,我會嘗試使用它,當提供一個類似於枚舉的場景時,假設這樣做是有意義的。再次感謝。 – JohnLBevan

+0

ps。這是第一次在回答其他人的問題時使用了這個問題 - 這個解決方案比我以前的方法要好得多 - 謝謝。 http://developer42.wordpress.com/2012/10/12/path-finder-in-c/ – JohnLBevan

+1

只是一個nitpicker的說明:在.NET中,你有一個模式與兩個不同的類,一個單數(如顏色或刷子)和一個複數,這是靜態的(如顏色或畫筆)。對於顏色:http://msdn.microsoft.com/fr-fr/library/vstudio/system.windows.media.colors.black.aspx – jv42

2

我可能是錯的,但我不認爲,編譯器提供了這樣的警告。你可以用一些單元測試來捕捉這些問題,這些單元測試調用上面的方法和所有可能的枚舉值(爲此使用Enum.GetValues())。每次開發人員添加一個枚舉成員並忘記修改所有switch語句時,至少有一個單元測試會失敗,並顯示「StupidProgrammerException」(順便說一下:我會拋出一個ArgumentOutOfRangeException)。

+0

'InvalidEnumArgumentException'會更適合。 –

3

我發現使用簡單枚舉一個「解決方案」。這就是生的,也許它可以用一個更好的設計改進:

bool allCasesHandled; 

switch (myEnumValue) 
{ 
    case MyEnum.Value1: 
     allCasesHandled = true; 
     break; 

    //default: 
    // allCasesHandled = true; 
    // break; 
} 
System.Diagnostics.Debug.WriteLine(allCasesHandled); 

如果您嘗試進行編譯,你會得到一個錯誤「使用未分配的變量」。

維護起來有點麻煩,但對於簡單的情況,它可能會很有用,特別是對於意圖失敗的行的評論。

+0

很好的訣竅 - 因爲沒有默認會留下一個路徑,其中變量分配可能會丟失。我已經給出了@Roelof,因爲我認爲他是最好的體系結構,但這是一個很好的竅門,可以在沒有太多開銷的情況下完成我所需的任務。 – JohnLBevan

+0

@JohnLBevan謝謝,你的投票是有道理的,這一切都取決於代碼的範圍。 – jv42

0

我想你可以爲StyleCop編寫規則並在PostBuild事件中運行它們,並且它們可以在構建窗口中輸出警告。我們一開始嘗試這樣做,以避免忽略方法的返回值,但從來沒有完成它。最後,我研究它,你需要分析IL,這並不總是有趣。當然,我想這取決於你對樂趣的定義。