如何使C#Switch语句使用IgnoreCase

Tol*_*san 73 c# switch-statement

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

我有例如:

string s = "house";
switch (s)
{
  case "houSe": s = "window";
}
Run Code Online (Sandbox Code Playgroud)

将获得价值"窗口".如何覆盖switch-case语句,以便使用ignoreCase比较字符串?

Nic*_*ver 72

一种更简单的方法是在进入switch语句之前对字符串进行小写,并使情况更低.

实际上,从纯粹的极端纳秒性能角度来看,鞋面要好一些,但不太自然.

例如:

string s = "house"; 
switch (s.ToLower()) { 
  case "house": 
    s = "window"; 
    break;
}
Run Code Online (Sandbox Code Playgroud)

  • 看来`ToUpper(Invariant)`不仅更快,而且更可靠:http://stackoverflow.com/a/2801521/67824 (8认同)
  • @Lazarus - 这是来自CLR通过C#,它在隐藏的功能线程中发布了一段时间:http://stackoverflow.com/questions/9033/hidden-features-of-c/12137#12137你可以用几百万次迭代激活LinqPad,这是真的. (6认同)
  • 是的,我知道小写是一种方法,但我希望它是ignoreCase。有没有办法可以覆盖 switch-case 语句? (2认同)
  • 8年后... https://twitter.com/Nick_Craver/status/970736005287264256 (2认同)

Jef*_*dge 57

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

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

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

另一种选择是做明显的if陈述链.这通常不会听起来那么糟糕,因为结构非常规律.

关于这一点的好处是,在与字符串进行比较时,模拟自己的开关功能并没有任何性能损失.系统不会像整数一样制作O(1)跳转表,因此无论如何它将一次比较每个字符串.

如果有许多情况要比较,并且性能是个问题,那么List<T>上面描述的选项可以用排序的字典或哈希表替换.然后,性能可能会匹配或超过switch语句选项.

以下是代表列表的示例:

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();
}
Run Code Online (Sandbox Code Playgroud)

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

如果您的每个案例的行为都不适合以这种方式委托调用,例如,如果需要不同的参数,那么您就会陷入链式的状态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";
    }
Run Code Online (Sandbox Code Playgroud)

  • 好.我将举例说明这是重要的.假设我们有"英语!"字样"café"而不是"house".这个值可以通过"caf\u00E9"或"cafe\u0301"同样表示(同样可能).使用`ToLower()`或`ToLowerInvariant()`的序数相等(如在switch语句中)将返回false.`StringComparison.InvariantCultureIgnoreCase`的`Equals`将返回true.由于两个序列在​​显示时看起来都相同,因此`ToLower()`版本是一个令人讨厌的错误.这就是为什么即使你不是土耳其语,最好进行正确的字符串比较. (46认同)
  • @Seth Petry-Johnson - 也许可以进行优化,但字符串比较选项融入框架的原因是我们不必全都成为语言学专家来编写正确的,可扩展的软件. (8认同)
  • 除非我弄错了,两者对于某些文化(如土耳其语)只是不同的,在这种情况下他不能使用`ToUpperInvariant()`或`ToLowerInvariant()`?此外,他没有比较_two unknown strings_,他将一个未知字符串与一个已知字符串进行比较.因此,只要他知道如何硬编码合适的大写或小写表示,那么切换块应该工作正常. (5认同)

STL*_*Dev 30

很抱歉这个新帖子提到了一个老问题,但是有一个新选项可以使用C#7(VS 2017)来解决这个问题.

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;
}
Run Code Online (Sandbox Code Playgroud)

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

顺便说一下,2017年2月在Visual Studio Magazine中有一篇有趣的文章描述了模式匹配以及如何在case块中使用它.请看一下:C#7.0案例块中的模式匹配

编辑

根据@ 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;
}
Run Code Online (Sandbox Code Playgroud)

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

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

  • 它会更长,但我更喜欢`switch (houseName)`然后进行类似于你所做的比较,即`case var name when name.Equals("MyHouse", ...` (2认同)
  • 您还可以使用“对象模式匹配”,例如“case {}when”,这样您就不必担心变量类型和名称。 (2认同)

uli*_*i78 29

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

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;
   }
}
Run Code Online (Sandbox Code Playgroud)

  • 我更喜欢这种解决方案,因为它不鼓励使用魔术字符串. (3认同)

Mag*_*nus 17

一种可能的方法是使用带有动作委托的忽略案例字典.

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

dic["HouSe"]();
Run Code Online (Sandbox Code Playgroud)

  • 而不是"CurrentCultureIgnoreCase",[`OrdinalIgnoreCase`](https://msdn.microsoft.com/en-us/library/ms973919.aspx)是首选. (2认同)
  • @richardEverett 首选?取决于你想要什么,如果你想要当前的文化忽略大小写,它不是首选。 (2认同)

Lew*_*isM 15

@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;
}
Run Code Online (Sandbox Code Playgroud)

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

  • +1 - 这应该是现代(C#7以后)开发的公认答案.我要做的一个改变就是我会这样编码:`case var name when"Bungalow".Equals(name,StringComparison.InvariantCultureIgnoreCase):`因为这可以防止空引用异常(其中houseName为null),或者添加字符串首先为null的情况. (3认同)
  • @maxp - 查看编辑历史记录 - 当我发布该评论时(3 年前),示例代码会导致 NullReferenceException。此后示例代码已被某人更改。 (3认同)

Gur*_*ron 11

我想说,通过switch 表达式(在 C# 8.0 中添加)、丢弃模式和本地函数, @STLDev@LewisM建议的方法可以以更干净/更短的方式重写:

string houseName = "house";  // value to be tested
// local method to compare, I prefer to put them at the bottom of the invoking method:
bool Compare(string right) => string.Equals(houseName, right, StringComparison.InvariantCultureIgnoreCase);
var s = houseName switch
{
    _ when Compare("Bungalow") => "Single glazed",
    _ when Compare("Church") => "Stained glass",
    //  ...
    _ => "No windows (cold or dark)" // default value
};
Run Code Online (Sandbox Code Playgroud)


小智 8

现在您可以使用switch 表达式(重写前面的示例):

return houseName switch
{
    _ when houseName.Equals("MyHouse", StringComparison.InvariantCultureIgnoreCase) => "MyWindow",
    _ when houseName.Equals("YourHouse", StringComparison.InvariantCultureIgnoreCase) => "YourWindow",
    _ when houseName.Equals("House", StringComparison.InvariantCultureIgnoreCase) => "Window",
    _ => null
};
Run Code Online (Sandbox Code Playgroud)