如何使C#Switch语句使用IgnoreCase

如果我有一个switch-case语句,其中交换机中的对象是string,是否有可能做ignoreCase比较呢?

我举个例子:

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

将获得价值的“窗口”。 如何覆盖switch-case语句,以便比较使用ignoreCase的string?

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

不幸的是, switch不做任何事情,只是一个有序的比较。 对某些types的应用程序进行序数比较是很好的,比如用严格定义的代码parsingASCII文件,但是对于大多数其他用途,序数string比较是错误的。

我过去为了获得正确的行为所做的只是模拟了我自己的开关语句。 有很多方法可以做到这一点。 一种方法是创build一个List<T>的成对string和委托。 该列表可以使用适当的string比较进行search。 当find匹配时,可以调用关联的委托。

另一个select是做明显的if语句链。 这通常不会像听起来那样糟糕,因为结构非常规整。

关于这一点的好处是,在与string进行比较时,嘲笑自己的交换机function并没有真正的性能损失。 系统不会像整数那样创build一个O(1)跳转表,因此无论如何都要逐个比较每个string。

如果有很多情况需要比较,并且性能是一个问题,那么上面描述的List<T>选项可以用sorting的字典或散列表replace。 那么性能可能会匹配或超过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(); } 

当然,你可能会想要向CustomSwitchDestination委托添加一些标准参数和一个返回types。 而且你会想要更好的名字!

如果您的每个案例的行为都不适合以这种方式进行委托调用,例如,如果不同的参数是必需的,那么您将被locking在链接的状态。 我也做了几次。

  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"; } 

一个更简单的方法就是在你的string进入switch语句之前降低它的值,并且降低这个值。

实际上,从纯粹的极端纳秒性能angular度来看,upper是好一点,但看起来不那么自然。

例如:

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

在某些情况下,使用枚举可能是一个好主意。 所以首先parsing枚举(与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; } } 

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

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

对不起,这个新的post老问题,但有一个新的select来解决这个问题,使用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; } 

这个解决scheme还解决了@Jeffrey L Whitledge的回答中提到的问题,即string不区分大小写的比较和比较两个小写string是不一样的。

顺便说一句,在2017年2月的Visual Studio杂志中有一篇有趣的文章描述了模式匹配以及如何在案例块中使用它。 请看看: C#7.0大小写块中的模式匹配

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

 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(); } }