如何使C#Switch语句使用IgnoreCase

时间:2010-02-25 13:07:13

标签: c# switch-statement

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

我有例如:

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

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

10 个答案:

答案 0 :(得分:70)

更简单的方法是在进入switch语句之前对字符串进行小写,并使案例更低。

实际上,从纯粹的极端纳秒性能角度来看,鞋帮要好一些,但看起来不那么自然。

E.g:

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

答案 1 :(得分:56)

您似乎意识到,小写两个字符串并进行比较与进行忽略大小写比较不同。这有很多原因。例如,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();
}

当然,您可能希望向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";
    }

答案 2 :(得分:28)

在某些情况下,使用枚举可能是个好主意。因此,首先解析枚举(使用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;
   }
}

答案 3 :(得分:23)

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

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

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

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

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

修改

根据@ 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;
}

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

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

答案 4 :(得分:16)

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

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

dic["HouSe"]();

答案 5 :(得分:8)

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

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

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

视觉工作室杂志的nice article on pattern matching case blocks可能值得一看。

答案 6 :(得分:1)

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

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

答案 7 :(得分:1)

以下是将@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();
    }
}

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

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

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

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

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

答案 8 :(得分:0)

这样做应该足够了:

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

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

答案 9 :(得分:-1)

10年后,使用C#模式匹配,您可以执行以下操作:

private string NormalisePropertyType(string propertyType) => true switch
{
    true when string.IsNullOrWhiteSpace(propertyType) => propertyType,
    true when "house".Equals(propertyType, StringComparison.OrdinalIgnoreCase) => "house",
    true when "window".Equals(propertyType, StringComparison.OrdinalIgnoreCase) => "window",
    true when "door".Equals(propertyType, StringComparison.OrdinalIgnoreCase) => "door",
    true when "roof".Equals(propertyType, StringComparison.OrdinalIgnoreCase) => "roof",
    true when "chair".Equals(propertyType, StringComparison.OrdinalIgnoreCase) => "chair",
    _ => propertyType
};