English 中文(简体)
如何使C#中的Switch语句忽略大小写
原标题:How to make C# Switch Statement use IgnoreCase

如果我有一个包含字符串的switch-case语句,是否可以进行忽略大小写的比较?

我有例如:

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

s会得到值"window"吗?如何覆盖switch-case语句,以便使用ignoreCase来比较字符串?

最佳回答

如你所知,减少两条插图和比较,与忽视案例的比较不同。 原因很多。 例如,统法协会编码标准允许有二分法文本以多种方式编码。 某些特性既包括基质特性,也包括单一代码点的分辨率。 这些特性也可以作为基本特征来体现,随后是分属性。 这两种代表的目的都是平等的,而文化意识的比较则体现在其中。 联系网框架将正确地确定它们是平等的,既包括当前文化,也包括无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";
    }
问题回答

一个更简单的方法是在进入开关语句之前将字符串转换为小写,并使案例变小。

实际上,从纯极极端的纳米秒业绩角度看,上层情况较好,但不太自然。

例如:

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

抱歉为了一个旧问题发了一篇新帖子,不过现在有一个新选项可以使用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;
}

这个解决方案还解决了 @Jeffrey L Whitledge 的回答中提到的问题,即字符串大小写不敏感的比较与比较两个小写字符串不同。

顺便提一下,Visual Studio杂志2017年2月有一篇有趣的文章介绍了模式匹配及其在Case块中的应用。请查看:C# 7.0 Case Block中的模式匹配

编辑

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

@STLDeveloperA的答案的延伸。在C# 7中,一种新的语句评估方法是使用模式匹配switch语句,而不是多个if语句,类似于@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;
}

Visual Studio 杂志有一篇有关模式匹配 case 块的精彩文章,值得一读。

在某些情况下,使用枚举可能是一个好主意。所以首先解析枚举(忽略大小写标志为 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;
   }
}

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

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

dic["HouSe"]();

// Note that the call doesn t return text, but only populates local variable s.
// If you want to return the actual text, replace Action to Func<string> and values in dictionary to something like () => "window2"

这是一个将@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语句的感觉非常接近)。

由于它在内部使用字典,它获得了O(1)的性能,并且不依赖于遍历字符串列表。当然,你需要构建那个字典,这可能会付出更多的代价。如果你想反复重用Switch行为,你可以创建和初始化SwitchCaseIndependent对象一次,然后无论你想用多少次,都可以使用它。

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

我想说,使用在C# 8.0版本中增加的switch表达式、discard模式和局部函数,可以使@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
};

这样应该就足够了。

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

该开关比较因此与文化无关。就我所知,这应该可以实现与C#7模式匹配解决方案相同的结果,但更加简洁。

我希望这有助于将整个扼杀物转化成具体案件,或将下级案例加以比较。

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

Using the Case Insensitive Comparison: Comparing strings while ignoring case.

switch (caseSwitch)
{
    case string s when s.Equals("someValue", StringComparison.InvariantCultureIgnoreCase):
        // ...
        break;
}

了解更多细节,请访问此链接:C#语句和表达式中的Switch Case When

现在你可以使用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
};




相关问题
Anyone feel like passing it forward?

I m the only developer in my company, and am getting along well as an autodidact, but I know I m missing out on the education one gets from working with and having code reviewed by more senior devs. ...

NSArray s, Primitive types and Boxing Oh My!

I m pretty new to the Objective-C world and I have a long history with .net/C# so naturally I m inclined to use my C# wits. Now here s the question: I feel really inclined to create some type of ...

C# Marshal / Pinvoke CBitmap?

I cannot figure out how to marshal a C++ CBitmap to a C# Bitmap or Image class. My import looks like this: [DllImport(@"test.dll", CharSet = CharSet.Unicode)] public static extern IntPtr ...

How to Use Ghostscript DLL to convert PDF to PDF/A

How to user GhostScript DLL to convert PDF to PDF/A. I know I kind of have to call the exported function of gsdll32.dll whose name is gsapi_init_with_args, but how do i pass the right arguments? BTW, ...

Linqy no matchy

Maybe it s something I m doing wrong. I m just learning Linq because I m bored. And so far so good. I made a little program and it basically just outputs all matches (foreach) into a label control. ...

热门标签