如何使 C# switch 语句使用 IgnoreCase?

发布于 2024-08-22 18:02:25 字数 262 浏览 4 评论 0原文

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

例如,我有:

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

s 会得到值“window”吗?

如何覆盖 switch-case 语句,以便它使用 IgnoreCase 比较字符串?

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

I have for instance:

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

Will s get the value "window"?

How do I override the switch-case statement so it will compare the strings using IgnoreCase?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

心欲静而疯不止 2024-08-29 18:02:25

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

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

例如:

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

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

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

E.g.:

string s = "house"; 
switch (s.ToLower()) { 
  case "house": 
    s = "window"; 
    break;
}
一紙繁鸢 2024-08-29 18:02:25

对于这个老问题的新帖子,我们深表歉意,但是有一个新选项可以使用 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 的答案中提到的问题,即字符串的不区分大小写的比较与比较两个字符串不同小写字符串。

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

编辑

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

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

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 now offers "pattern matching", and it can be used to address this issue thusly:

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.

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

EDIT

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

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 switch statement, as it is with the classical use of the switch statement. Also, the temporary values declared in the case statement can prevent unwanted or inadvertent changes to the original value:

switch(houseName)
{
    case string hn when hn.Equals("X", StringComparison.InvariantCultureIgnoreCase):
        windowName = "X-Window";
        break;
}
离旧人 2024-08-29 18:02:25

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

不幸的是,switch 除了序数比较之外不执行任何操作。序数比较适用于某些类型的应用程序,例如使用严格定义的代码解析 ASCII 文件,但序数字符串比较对于大多数其他用途来说是错误的。

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

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

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

如果有很多情况需要比较,并且性能是一个问题,那么上面描述的List选项可以用排序字典或哈希表代替。那么性能可能会匹配或超过 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";
    }

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.

Unfortunately, switch doesn'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.

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.

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

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.

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.

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!

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 if statments. I’ve also done this a few times.

    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";
    }
玩套路吗 2024-08-29 18:02:25

@STLDev 的回答的扩展。

从 C# 7 开始,一种无需多个 if 语句即可执行语句评估的新方法是使用模式匹配 switch 语句,类似于 @STLDev,但这种方法是切换要切换的变量。

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 杂志有一篇关于模式匹配大小写块的好文章 这可能值得一看。

An extension to the answer by @STLDev.

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 @STLDev though this way is switching on the variable being switched.

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 blocks that might be worth a look.

影子的影子 2024-08-29 18:02:25

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

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.

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;
    }
}
感情洁癖 2024-08-29 18:02:25

一种可能的方法是将忽略大小写字典与操作委托一起使用。

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

dic["HouSe"]();

// 请注意,调用不返回文本,而仅填充局部变量 s。
// 如果要返回实际文本,请将 Action 替换为 Func 并将字典中的值替换为 () =>; “窗口2”

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"]();

// 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"

盛夏已如深秋| 2024-08-29 18:02:25

我想说的是 切换表达式 (在 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
};

I would say that with switch expressions (added in C# 8.0), discard patterns and local functions the approaches suggested by @STLDev and @LewisM can be rewritten in even more clean/shorter way:

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
};
水晶透心 2024-08-29 18:02:25

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

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

Now you can use the switch expression (rewrote the previous example):

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
};
故事与诗 2024-08-29 18:02:25

这是一个将 @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) 方法来简单地调用字典的 ContainsKey 方法可能是有意义的。

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

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(
quot;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:

   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).

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. If you want to reuse the Switch behavior over and over, you can create and initialize the the SwitchCaseIndependent object once and then use it as many times as you want.

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

养猫人 2024-08-29 18:02:25

这样做应该足够了:

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

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

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.

谁的新欢旧爱 2024-08-29 18:02:25

尝试将整个字符串转换为特定的大小写,小写或大写,然后用它进行比较:

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

Try to convert the whole string into a particular case, either lower case or upper case, and then use it 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();
    }
}
岁月静好 2024-08-29 18:02:25

使用不区分大小写的比较:
比较字符串时忽略大小写。

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

有关更多详细信息,请访问此链接:在 C# 语句和表达式中切换大小写

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

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

for more detail Visit this link: Switch Case When In C# Statement And Expression

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文