'Using .StartsWith in a Switch statement?

I'm working on a Switch statement and with two of the conditions I need to see if the values start with a specific value. The Switch statement does like this. The error says "cannot covert type bool to string".

Anyone know if I can use the StartsWith in a Switch or do I need to use If...Else statements?

switch(subArea)
            {
                case "4100":
                case "4101":
                case "4102":
                case "4200":
                    return "ABC";
                case "600A":
                    return "XWZ";
                case subArea.StartsWith("3*"):
                case subArea.StartsWith("03*"):
                    return "123";
                default:
                    return "ABCXYZ123";
            }


Solution 1:[1]

EDIT: If you are using C# >= 7, take a look at this answer first.


You are switching a String, and subArea.StartsWith() returns a Boolean, that's why you can't do it. I suggest you do it like this:

if (subArea.StartsWith("3*") || subArea.StartsWith("03*"))
    return "123";

switch(subArea)
{
    case "4100":
    case "4101":
    case "4102":
    case "4200":
        return "ABC";
    case "600A":
        return "XWZ";
    default:
        return "ABCXYZ123";
}

The result will be the same.

Solution 2:[2]

Since C# 7 you can do the following:

switch(subArea)
{
    case "4100":
    case "4101":
    case "4102":
    case "4200":
       return "ABC";
    case "600A":
       return "XWZ";
    case string s when s.StartsWith("3*"):
       return "123";
    case string s when s.StartsWith("03*"):
       return "123";
    default:
       return "ABCXYZ123";
}

Solution 3:[3]

Thanks to the when clause, you can now do:

switch (subArea)
{
    // Skipping regular cases with string literals
    case string dummy
        when subArea.StartsWith("3*") ||
             subArea.StartsWith("03*"):
        return "123";
    default:
        return "ABCXYZ123";
}

Solution 4:[4]

Since C# 9 you can do the following:

switch(subArea)
{
    case "4100":
    case "4101":
    case "4102":
    case "4200":
        return "ABC";
    case "600A":
        return "XWZ";
    case { } when subArea.StartsWith("3*"):
    case { } when subArea.StartsWith("03*"):
        return "123";
    default:
        return "ABCXYZ123";
}

Solution 5:[5]

The case labels must be strings, since the switch expression is a string; however, StartsWith returns a Boolean. I suggest handling these special cases in the default section.

switch(subArea)
{
    case "4100":
    case "4101":
    case "4102":
    case "4200":
        return "ABC";
    case "600A":
        return "XWZ";
    default:
        if (subArea.StartsWith("3") || subArea.StartsWith("03")) {
            return "123";
        }
        return "ABCXYZ123";
}

Also the star (*) is probably wrong, unless you want subArea to contain it. StartWith does not accept wildcards.

Alternatively you could use regex:

if (Regex.IsMatch(subArea, "^3|^03")) { // or "^(3|03)"
    return "123";
}

where ^ means start of line and | means or.

Solution 6:[6]

Joe kind of beat me to it, but here's another non-switch way of doing it, which essentially implements a pattern matching algorithm with a rule set.

private static string GetSomeStringOrOther(string subArea)
{
    // Create a set of pattern matching functions...
    Func<string, string, bool> matchEquals = (a, b) => a.Equals(b);
    Func<string, string, bool> matchStarts = (a, b) => a.StartsWith(b);

    // Create a rule set...
    Tuple<string, string, Func<string, string, bool>>[] cases = new []
    {
        new Tuple<string, string, Func<string, string, bool>>("4100", "ABC", matchEquals),
        new Tuple<string, string, Func<string, string, bool>>("4101", "ABC", matchEquals),
        new Tuple<string, string, Func<string, string, bool>>("4102", "ABC", matchEquals),
        new Tuple<string, string, Func<string, string, bool>>("4200", "ABC", matchEquals),
        new Tuple<string, string, Func<string, string, bool>>("600A", "XWZ", matchEquals),
        new Tuple<string, string, Func<string, string, bool>>("3*", "123", matchStarts),
        new Tuple<string, string, Func<string, string, bool>>("03*", "123", matchStarts),
    };

    // Look for a match...
    foreach(var matchCase in cases)
    {
        if(matchCase.Item3(subArea, matchCase.Item1))
        {
            // Return if it matches...
            return matchCase.Item2;
        }
    }

    // Otherwise return the default...
    return "ABCXYZ123";
}

Advantages

  • If you need a new rule, it's easy to add to the rule set.
  • If you need a new pattern matching function, again, easy to add.
  • Doesn't need extensive rework if a rule changes.

Disadvantages

  • Novice/Beginner and even some intermediate developers might not have a clue what's going on.

Improvements

  • Replace Tuple<string, string, Func<string, string, bool>> with a semantic object that represents a Rule

Solution 7:[7]

Just for fun, here's another solution that avoids the switch statement.

var map = new[] {
    new { Value = "4100", StartsWith = false, Result="ABC" },
    new { Value = "4101", StartsWith = false, Result="ABC" },
    new { Value = "4102", StartsWith = false, Result="ABC" },
    new { Value = "4200", StartsWith = false, Result="ABC" },
    new { Value = "600A", StartsWith = false, Result="XWZ" },
    new { Value = "3*", StartsWith = true, Result="123" },
    new { Value = "03*", StartsWith = true, Result="123" },
};

var subarea = ... whatever ...;

var result = map.Where(e =>
        {
            if (e.StartsWith)
            {
                return subarea.StartsWith(e.Value);
            }
            else
            {
                return subarea == e.Value;
            }
        }
    )
    .Select(e => e.Result)
    .FirstOrDefault() ?? "ABCXZ123";

The order in the array map determines the priority, so that, for example, you can have an exact match on, say, "3*11", as well as a StartsWith match on "3*", e.g.:

var map = new[] {
    new { Value = "3*11", StartsWith = false, Result="ABC" },
    new { Value = "4100", StartsWith = false, Result="ABC" },
    new { Value = "4101", StartsWith = false, Result="ABC" },
    new { Value = "4102", StartsWith = false, Result="ABC" },
    new { Value = "4200", StartsWith = false, Result="ABC" },
    new { Value = "600A", StartsWith = false, Result="XWZ" },
    new { Value = "3*", StartsWith = true, Result="123" },
    new { Value = "03*", StartsWith = true, Result="123" },
};

Solution 8:[8]

With LINQ, the nice answer by @seriesOne can be "simplified" a bit by replacing the foreach and return statements with:

// using System.Linq;

// Look for a match...
var result = cases
    .Where(c => c.Item3(subArea, c.Item1))
    .FirstOrDefault();

// Return the match or the default.
return result == null ? "ABCXYZ123" : result.Item2;

Solution 9:[9]

Since C# 9 you can also do something like this, with a switch expression instead of a statement:

 return subArea switch
        {
            "4100" or "4101" or "4102" or "4200" => "ABC",
            "600A" => "XWZ",
            { } when subArea.StartsWith("3*") || subArea.StartsWith("03*") => "123",
            _ => "ABCXYZ123"
        };

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Steve Rousseau
Solution 3 Pang
Solution 4 Nwy
Solution 5
Solution 6
Solution 7 Joe
Solution 8
Solution 9 Robledo