簡體   English   中英

如果 switch(enum_val) 缺少 case 語句,有沒有辦法讓 C# 編譯器發出錯誤?

[英]Is there a way to get the C# compiler to emit an error if a switch(enum_val) is missing a case statement?

我剛剛意識到我在枚舉中的“必須處理”值列表中添加了一個值,但直到運行時我才發現它。 我知道 C# 編譯器在類型的反射和自省方面非常強大,所以我想知道是否有辦法強制switch / case語句覆蓋所有可能的enum值?

例子:

enum Colors
{
   Red,
   Blue,
   Green,
   Yellow
};

Colors c = ...;

switch (c)
{
   case Colors.Red:  // No error, Red is a Color
      break;
   case Colors.Blue:
   case Colors.Green:  // No error, Blue and Green handled as well
      break;
}  // whoops! "error: 'Colors.Yellow' unhandled"
   // or even, "error: no 'default' and 'Colors.Yellow' unhandled"

我想要一個編譯時解決方案。

您可以使用SwitchEnumAnalyzer獲取此類編譯器時警告。 我剛剛在我的項目中使用它,它工作得很好。 與任何 Roslyn 分析器一樣,您可以選擇通知級別 - 如果它應該只是警告,還是適當的錯誤。

不,沒有編譯時方法可以做到這一點。 然而,非常簡單的答案是有一個default處理程序,它簡單地拋出一個異常,“這個選項沒有被處理,噓”。

switch (c)
{
    case Colors.Red:  // no error, Red is a Color
        break;
    case Colors.Blue:
    case Colors.Green:  // no error, Blue and Green handled as well
        break;
    default:
        throw new Exception("Unhandled option: " + c.ToString());
}

這正是為什么我們需要在所有解決方案中進行測試的最佳示例。 您必須編寫一個可以枚舉您的 Enum 並調用包含 switch case 的方法的測試。 使用此測試,每次編輯 Enum 但忘記更新 switch case 時,您都會得到一個失敗的測試。

    [Test]
    public void CheckThatAllEnumCoveredInSwitchCase()
    {

        foreach (var singleEnum in Enum.GetValues(typeof(YOURENUM)))
        {
                myclass.methodofswitchcase((YOURENUM)singleEnum);
        }

    }

如果枚舉大小,您可以盡早拋出異常,希望在對枚舉進行更改時盡早提醒您:

enum MyEnum {A, B};

class TestEnum
{
    // Static constructor
    static TestEnum()
    {
        // Check if this code needs updating as the enum has changed
        if (Enum.GetNames(typeof(MyEnum)).Length != 2)
        {
            // If this fails update myFunction and the number above
            throw new Exception("Internal error - code inconsistency");
        }
    }

    // My function that switches on the enum
    string myFunction(MyEnum myEnum)
    {
        switch (myEnum)
        {
            case MyEnum.A: return "A";
            case MyEnum.B: return "B";
        }
        throw new Exception("Internal error - missing case");
    }
}

如果枚舉中的項目數發生更改,這將從靜態構造函數中引發異常。 所以開發人員知道他需要更新代碼。 您甚至可以通過構建時運行的單元測試來執行此檢查。

C# 編譯器沒有內置此檢查,但有一個適用於 .Net 的代碼合同檢查器: https ://blogs.msdn.microsoft.com/francesco/2014/09/12/how-to-use -cccheck-to-prove-no-case-is-forgotten/

該技術是使用代碼契約斷言來告訴檢查器default情況永遠不應該達到:

switch (c) {
  case Colors.Red: break;
  case Colors.Blue:
  case Colors.Green: break;
  default:
    Contract.Assert(false); // Tell static checker this shouldn't happen
}

然后,如果檢查器發現它是可訪問的(因為未處理枚舉值之一),它會警告您:

warning : CodeContracts: This assert, always leading to an error, may be reachable.
Are you missing an enum case?

是的,現在可以了

(Roslyn) 編譯器現在具有內置檢查功能,因此您不再需要添加自定義分析器。 添加到您的.editorconfig

dotnet_diagnostic.CS8509.severity=error # missing switch case for named enum values
dotnet_diagnostic.CS8524.severity=none # missing switch case for unnamed enum values

如果缺少命名枚舉值大小寫,您將收到編譯時錯誤。

public enum E { Hi = 1, Bye = 2 }

與某處

return someValue switch
{
    E.Hi => "Welcome!",
    E.Bye => "Thanks for your visit!"
};

不會導致警告或編譯器錯誤,因為已處理命名值E.HiE.Bye 刪除其中一種情況將導致編譯器錯誤。

但是,請注意枚舉的基礎類型(默認情況下)是int ,因此未涵蓋開關中實際可能的情況 - 在上面的示例中, (E)0或其他整數值 ( (E)3等)可能會導致運行時錯誤。 像上面這樣的switch 表達式將拋出System.Runtime.CompilerServices.SwitchExpressionException ,但不會拋出switch 語句(因此可能會導致稍后出現運行時錯誤)。 當然,您可以為此添加一個檢查(而不是使用默認情況,因為這會破壞我們獲取編譯器錯誤的嘗試):

if (!Enum.IsDefined<E>(someValue))
{
    throw new Exception($"Enum E does not have a value '{someValue}'");
}
switch (someValue)
{
    case E.Hi:
        DoSomeThing();
        return "Welcome!";
    case E.Bye:
        DoSomeThingElse();
        return "Thanks for your visit!";
};

您可以使用在運行時檢查的元方法,但至少檢查整個開關。

https://github.com/faisalmansoor/MiscUtil/blob/master/EnumSwitch/EnumSwitch.cs

對於 C++,您可以通過將編譯器開關 /we4061 添加到項目設置中來讓編譯器發出警告。 希望這可以幫助。 :)

http://msdn.microsoft.com/en-us/library/thxezb7y.aspx

http://msdn.microsoft.com/en-us/library/96f5t7fy(v=vs.80).aspx

實際上我錯了。 看來您可以讓編譯器拋出錯誤。 只需使用 /Wall /we4061 編譯。 我自己沒有嘗試過,但是通過閱讀上面的 MSDN 頁面,看起來這應該可行。

只需拋出(無意的雙關語)這個,您就可以用字典替換 Switch-Case(例如Func<int,int> ):

Dictionary<Colors,Func<int,int>> d = new Dictionary<Colors, Func<int, int>>();
d.Add(Colors.Red, (x) => x+1);
d.Add(Colors.Blue, (x) => x+1);
d.Add(Colors.Green, (x) => x+1);
foreach (Colors color in Enum.GetValues(typeof(Colors)))
{
    if (!d.ContainsKey(color))
    {
        throw new Exception("Poor color " + color + " ignored");
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM