如何让 C# Switch 语句使用 IgnoreCase

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2334134/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-07 01:31:47  来源:igfitidea点击:

How to make the C# Switch Statement use IgnoreCase

c#switch-statement

提问by Tolsan

If I have a switch-case statement where the object in the switch is string, is it possible to do anyway ignoreCase compare?

如果我有一个 switch-case 语句,其中 switch 中的对象是字符串,是否可以进行 ignoreCase 比较?

I have for instance:

我有例如:

string s = "house";
switch (s)
{
  case "houSe": s = "window";
}

Will s get value "window". How to override the switch-case statement so it will compare the strings using ignoreCase?

将获得价值“窗口”。如何覆盖 switch-case 语句以便它使用 ignoreCase 比较字符串?

采纳答案by Jeffrey L Whitledge

As you seem to be aware, lowercasing two strings and comparing them is not the same as doing an ignore-case comparison. There are lots of reasons for this. For example, the Unicode standard allows text with diacritics to be encoded multiple ways. Some characters includes both the base character and the diacritic in a single code point. These characters may also be represented as the base character followed by a combining diacritic character. These two representations are equal for all purposes, and the culture-aware string comparisons in the .NET Framework will correctly identify them as equal, with either the CurrentCulture or the InvariantCulture (with or without IgnoreCase). An ordinal comparison, on the other hand, will incorrectly regard them as unequal.

正如您似乎知道的那样,小写两个字符串并比较它们与进行忽略大小写比较不同。这有很多原因。例如,Unicode 标准允许以多种方式对带有变音符号的文本进行编码。某些字符在单个代码点中同时包含基本字符和变音符号。这些字符也可以表示为基本字符后跟一个组合变音符号。这两种表示对于所有目的都是相等的,并且 .NET Framework 中的文化感知字符串比较将正确地将它们识别为相等,无论是 CurrentCulture 还是 InvariantCulture(有或没有 IgnoreCase)。另一方面,顺序比较会错误地将它们视为不相等。

Unfortunately, switchdoesn't do anything but an ordinal comparison. An ordinal comparison is fine for certain kinds of applications, like parsing an ASCII file with rigidly defined codes, but ordinal string comparison is wrong for most other uses.

不幸的是,switch除了顺序比较之外什么都不做。序数比较适用于某些类型的应用程序,例如解析具有严格定义的代码的 ASCII 文件,但序数字符串比较对于大多数其他用途是错误的。

What I have done in the past to get the correct behavior is just mock up my own switch statement. There are lots of ways to do this. One way would be to create a List<T>of pairs of case strings and delegates. The list can be searched using the proper string comparison. When the match is found then the associated delegate may be invoked.

我过去为获得正确行为所做的只是模拟我自己的 switch 语句。有很多方法可以做到这一点。一种方法是创建一List<T>对 case 字符串和委托。可以使用适当的字符串比较来搜索列表。当找到匹配时,可以调用关联的委托。

Another option is to do the obvious chain of ifstatements. This usually turns out to be not as bad as it sounds, since the structure is very regular.

另一种选择是执行明显的if语句链。这通常没有听起来那么糟糕,因为结构非常规则。

The great thing about this is that there isn't really any performance penalty in mocking up your own switch functionality when comparing against strings. The system isn't going to make a O(1) jump table the way it can with integers, so it's going to be comparing each string one at a time anyway.

这样做的好处是,在与字符串进行比较时,模拟您自己的开关功能并没有真正的任何性能损失。系统不会像处理整数那样创建 O(1) 跳转表,因此无论如何它都会一次比较每个字符串。

If there are many cases to be compared, and performance is an issue, then the List<T>option described above could be replaced with a sorted dictionary or hash table. Then the performance may potentially match or exceed the switch statement option.

如果要比较的情况很多,并且性能是一个问题,那么List<T>可以将上述选项替换为排序字典或哈希表。那么性能可能会匹配或超过 switch 语句选项。

Here is an example of the list of delegates:

以下是代表名单的示例:

delegate void CustomSwitchDestination();
List<KeyValuePair<string, CustomSwitchDestination>> customSwitchList;
CustomSwitchDestination defaultSwitchDestination = new CustomSwitchDestination(NoMatchFound);
void CustomSwitch(string value)
{
    foreach (var switchOption in customSwitchList)
        if (switchOption.Key.Equals(value, StringComparison.InvariantCultureIgnoreCase))
        {
            switchOption.Value.Invoke();
            return;
        }
    defaultSwitchDestination.Invoke();
}

Of course, you will probably want to add some standard parameters and possibly a return type to the CustomSwitchDestination delegate. And you'll want to make better names!

当然,您可能希望向 CustomSwitchDestination 委托添加一些标准参数和可能的返回类型。你会想要更好的名字!

If the behavior of each of your cases is not amenable to delegate invocation in this manner, such as if differnt parameters are necessary, then you're stuck with chained ifstatments. I've also done this a few times.

如果您的每个案例的行为都不适合以这种方式委托调用,例如如果需要不同的参数,那么您就会陷入链式if语句的困境。我也这样做过几次。

    if (s.Equals("house", StringComparison.InvariantCultureIgnoreCase))
    {
        s = "window";
    }
    else if (s.Equals("business", StringComparison.InvariantCultureIgnoreCase))
    {
        s = "really big window";
    }
    else if (s.Equals("school", StringComparison.InvariantCultureIgnoreCase))
    {
        s = "broken window";
    }

回答by Nick Craver

A simpler approach is just lowercasing your string before it goes into the switch statement, and have the cases lower.

一种更简单的方法是在字符串进入 switch 语句之前将其小写,并将大小写降低。

Actually, upper is a bit better from a pure extreme nanosecond performance standpoint, but less natural to look at.

实际上,从纯粹的极端纳秒性能的角度来看,upper 更好一些,但看起来不太自然。

E.g.:

例如:

string s = "house"; 
switch (s.ToLower()) { 
  case "house": 
    s = "window"; 
    break;
}

回答by uli78

In some cases it might be a good idea to use an enum. So first parse the enum (with ignoreCase flag true) and than have a switch on the enum.

在某些情况下,使用枚举可能是个好主意。所以首先解析枚举(使用 ignoreCase 标志为真),然后在枚举上切换。

SampleEnum Result;
bool Success = SampleEnum.TryParse(inputText, true, out Result);
if(!Success){
     //value was not in the enum values
}else{
   switch (Result) {
      case SampleEnum.Value1:
      break;
      case SampleEnum.Value2:
      break;
      default:
      //do default behaviour
      break;
   }
}

回答by Magnus

One possible way would be to use an ignore case dictionary with an action delegate.

一种可能的方法是使用带有动作委托的忽略大小写字典。

string s = null;
var dic = new Dictionary<string, Action>(StringComparer.CurrentCultureIgnoreCase)
{
    {"house",  () => s = "window"},
    {"house2", () => s = "window2"}
};

dic["HouSe"]();

回答by UnknownFellowCoder

I hope this helps try to convert the whole string into particular case either lower case or Upper case and use the Lowercase string for comparison:

我希望这有助于尝试将整个字符串转换为小写或大写的特殊情况,并使用小写字符串进行比较:

public string ConvertMeasurements(string unitType, string value)
{
    switch (unitType.ToLower())
    {
        case "mmol/l": return (Double.Parse(value) * 0.0555).ToString();
        case "mg/dl": return (double.Parse(value) * 18.0182).ToString();
    }
}

回答by STLDev

Sorry for this new post to an old question, but there is a new option for solving this problem using C# 7 (VS 2017).

抱歉,这篇新帖子是针对旧问题的,但有一个新选项可以使用 C# 7 (VS 2017) 解决此问题。

C# 7 now offers "pattern matching", and it can be used to address this issue thusly:

C# 7 现在提供了“模式匹配”,它可以用来解决这个问题:

string houseName = "house";  // value to be tested, ignoring case
string windowName;   // switch block will set value here

switch (true)
{
    case bool b when houseName.Equals("MyHouse", StringComparison.InvariantCultureIgnoreCase): 
        windowName = "MyWindow";
        break;
    case bool b when houseName.Equals("YourHouse", StringComparison.InvariantCultureIgnoreCase): 
        windowName = "YourWindow";
        break;
    case bool b when houseName.Equals("House", StringComparison.InvariantCultureIgnoreCase): 
        windowName = "Window";
        break;
    default:
        windowName = null;
        break;
}

This solution also deals with the issue mentioned in the answer by @Jeffrey L Whitledge that case-insensitive comparison of strings is not the same as comparing two lower-cased strings.

此解决方案还处理@Jeffrey L Whitledge 在回答中提到的问题,即不区分大小写的字符串比较与比较两个小写字符串不同。

By the way, there was an interesting article in February 2017 in Visual Studio Magazine describing pattern matching and how it can be used in case blocks. Please have a look: Pattern Matching in C# 7.0 Case Blocks

顺便说一下,2017 年 2 月 Visual Studio 杂志上有一篇有趣的文章,描述了模式匹配以及如何在 case 块中使用它。请查看:C# 7.0 Case Blocks 中的模式匹配

EDIT

编辑

In light of @LewisM's answer, it's important to point out that the switchstatement has some new, interesting behavior. That is that if your casestatement contains a variable declaration, then the value specified in the switchpart is copied into the variable declared in the case. In the following example, the value trueis copied into the local variable b. Further to that, the variable bis unused, and exists only so that the whenclause to the casestatement can exist:

根据@LewisM 的回答,重要的是要指出该switch语句有一些新的、有趣的行为。也就是说,如果您的case语句包含变量声明,则该switch部分中指定的值将复制到case. 在以下示例中,值true被复制到局部变量中b。此外,该变量b未使用,并且仅存在以便语句的when子句case可以存在:

switch(true)
{
    case bool b when houseName.Equals("X", StringComparison.InvariantCultureIgnoreCase):
        windowName = "X-Window";):
        break;
}

As @LewisM points out, this can be used to benefit - that benefit being that the thing being compared is actually in the switchstatement, as it is with the classical use of the switchstatement. Also, the temporary values declared in the casestatement can prevent unwanted or inadvertent changes to the original value:

正如@LewisM 指出的那样,这可以用来受益 - 好处是被比较的事物实际上在switch语句中,就像语句的经典用法一样switch。此外,case语句中声明的临时值可以防止对原始值进行不必要的或无意的更改:

switch(houseName)
{
    case string hn when hn.Equals("X", StringComparison.InvariantCultureIgnoreCase):
        windowName = "X-Window";
        break;
}

回答by LewisM

An extension to the answer by @STLDeveloperA. A new way to do statement evaluation without multiple if statements as of c# 7 is using the pattern matching Switch statement, similar to the way @STLDeveloper though this way is switching on the variable being switched

@STLDeveloperA 对答案的扩展。从 c# 7 开始,在没有多个 if 语句的情况下进行语句评估的新方法是使用模式匹配 Switch 语句,类似于@STLDeveloper 的方式,尽管这种方式是在正在切换的变量上进行切换

string houseName = "house";  // value to be tested
string s;
switch (houseName)
{
    case var name when string.Equals(name, "Bungalow", StringComparison.InvariantCultureIgnoreCase): 
        s = "Single glazed";
    break;

    case var name when string.Equals(name, "Church", StringComparison.InvariantCultureIgnoreCase):
        s = "Stained glass";
        break;
        ...
    default:
        s = "No windows (cold or dark)";
        break;
}

The visual studio magazine has a nice article on pattern matching case blocksthat might be worth a look.

视觉工作室杂志有一篇关于模式匹配案例块好文章,可能值得一看。

回答by Flydog57

Here's a solution that wraps @Magnus 's solution in a class:

这是一个将 @Magnus 的解决方案包装在一个类中的解决方案:

public class SwitchCaseIndependent : IEnumerable<KeyValuePair<string, Action>>
{
    private readonly Dictionary<string, Action> _cases = new Dictionary<string, Action>(StringComparer.OrdinalIgnoreCase);

    public void Add(string theCase, Action theResult)
    {
        _cases.Add(theCase, theResult);
    }

    public Action this[string whichCase]
    {
        get
        {
            if (!_cases.ContainsKey(whichCase))
            {
                throw new ArgumentException($"Error in SwitchCaseIndependent, \"{whichCase}\" is not a valid option");
            }
            //otherwise
            return _cases[whichCase];
        }
    }

    public IEnumerator<KeyValuePair<string, Action>> GetEnumerator()
    {
        return _cases.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return _cases.GetEnumerator();
    }
}

Here's an example of using it in a simple Windows Form's app:

下面是在一个简单的 Windows 窗体应用程序中使用它的示例:

   var mySwitch = new SwitchCaseIndependent
   {
       {"hello", () => MessageBox.Show("hello")},
       {"Goodbye", () => MessageBox.Show("Goodbye")},
       {"SoLong", () => MessageBox.Show("SoLong")},
   };
   mySwitch["HELLO"]();

If you use lambdas (like the example), you get closures which will capture your local variables (pretty close to the feeling you get from a switch statement).

如果您使用 lambdas(如示例),您将获得将捕获您的局部变量的闭包(非常接近您从 switch 语句中获得的感觉)。

Since it uses a Dictionary under the covers, it gets O(1) behavior and doesn't rely on walking through the list of strings. Of course, you need to construct that dictionary, and that probably costs more.

由于它在幕后使用字典,因此它的行为为 O(1),并且不依赖于遍历字符串列表。当然,您需要构建该字典,而这可能会花费更多。

It would probably make sense to add a simple bool ContainsCase(string aCase)method that simply calls the dictionary's ContainsKeymethod.

添加一个简单的bool ContainsCase(string aCase)方法来简单地调用字典的ContainsKey方法可能是有意义的。

回答by Kevin Bennett

It should be sufficient to do this:

这样做应该就足够了:

string s = "houSe";
switch (s.ToLowerInvariant())
{
  case "house": s = "window";
  break;
}

The switch comparison is thereby culture invariant. As far as I can see this should achieve the same result as the C#7 Pattern-Matching solutions, but more succinctly.

切换比较因此是文化不变的。据我所知,这应该与 C#7 模式匹配解决方案达到相同的结果,但更简洁。