简体   繁体   中英

Can I use a switch statement in conjunction with string.Contains()?

I have a method using a long string of if/elseif statements (about 10-15), and I understand that when you get past about 5 if/else if statements, it's better to use a switch. That being said, I'm not sure that I can use a switch statement in my case, because my if/else if statements rely on testing a string, not for equality, but using the Contains() method. So, right now I have something equivalent of

string s = "ABCD";
if(s.Contains("A")
{
    //do stuff
}
else if(s.Contains("E")
{
    //do different stuff
}
etc ...

I've tried differnt ways of implementing the switch statement, such as

switch()
{
    case(s.Contains("A"))
    {
        //do stuff
    }
}

But each way I've tried results in a syntax error.

Is there a way to use a switch statement while still testing the string using the Contains() method?

No, switch statements require constant values for the case labels. So in general you'd be better off sticking with the if statements. But this is about as close as you can get to using a switch statement for your scenario.

string myString = "ABC";
List<string> subStrings = new List<string>{"A", "B", "C"};
switch (subStrings.FirstOrDefault(myString.Contains))
{
    case "A":
        Console.WriteLine("Has A");
        break;
    case "B":
        Console.WriteLine("Has B");
        break;
    case "C":
        Console.WriteLine("Has C");
        break;
    default:
        Console.WriteLine("No ABC");
        break;
}

I doubt that would be any faster than the if statements, because the FirstOrDefault is basically doing the same thing, and it breaks the DRY principal as it requires updates to the list and switch statement.

The first thing that comes to mind is

        string s = "ABCD";

        foreach (char oneChar in s.ToCharArray())
        {
            switch (oneChar)
            {
                case 'A':
                    Console.WriteLine("Do A stuff");
                    break;
                case 'B':
                    Console.WriteLine("Do B stuff");
                    break;
                case 'C':
                    Console.WriteLine("Do C stuff");
                    break;
                case 'D':
                    Console.WriteLine("Do D stuff");
                    break;
                default:
                    break;
            }
        }

depending on what you are doing, that may or may not be an efficient way to go. If a majority of the characters in the string actually cause a branch to one of these cases, this is much more efficient as you will not be having to do a bunch of string contains searches. If there are a lot of characters in the string that have no matching branch (they do nothing), then this is probably not an efficient way to go.

having a ton of if/else checks for specific stuff, usually alerts me of a good place to use enumerables for flexibility later if you can use it in your scenario. i'd probably do something like this:

string input = "ABDE";
var mapping = new Dictionary<Func<string, bool>, Action<string>>()
{
    { (string i) => i.Contains("A"), (string i) => Console.WriteLine("Found input with 'A'") },
    { (string i) => i.Contains("B"), (string i) => Console.WriteLine("Found input with 'B'") },
    { (string i) => i.Contains("C"), (string i) => Console.WriteLine("Found input with 'C'") },
    { (string i) => i.Contains("D"), (string i) => Console.WriteLine("Found input with 'D'") }
};

foreach (var criteria in mapping)
{
    if (criteria.Key(input)) {
        criteria.Value(input);
        break;
    }
}

That way the test/action conditions are grouped together, you can cleanly run over all the rules with a foreach without much work, and adding/removing rules is easier.

Using the pattern matching feature in C# 7.0, it is now possible to use Contains in switch statements. Provides much cleaner code than multiple if else blocks. The following code sample shows how to do it

        var patterns = new[] { "dog", "cat", "cats and dogs", "parrot", "parrot and cat" };

        foreach (var item in patterns)
        {
            switch (item)
            {
                case var s when s.Contains("cat"):
                    Console.WriteLine($"{item} contains cat");
                    break;

                case var s when s.Contains("dog"):
                    Console.WriteLine($"{item} contains dog");
                    break;

                case var s when s.Contains("parrot"):
                    Console.WriteLine($"{item} contains parrot");
                    break;
            }
        }

Please note, it won't work with versions earlier than C# 7.0.

If you have a LOT of these lines, sometimes it can be clearer and more flexible to use a dictionary to map the target values to actions, for example:

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApplication1
{
    public class Program
    {
        static void Main()
        {
            var dict = new Dictionary<string, Action<string>>
            {
                ["A"] = Console.WriteLine,
                ["B"] = doSomething1,
                ["C"] = doSomething2,
                ["D"] = str => Console.WriteLine("Inline: " + str)
            };

            string s = "ABCD";
            string first = dict.Keys.FirstOrDefault(t => s.Contains(t));

            if (first != null)
                dict[first](first);
            else
                ; // Default behaviour.
        }

        private static void doSomething1(string x)
        {
            Console.WriteLine("doSomething1 with " + x);
        }

        private static void doSomething2(string x)
        {
            Console.WriteLine("doSomething2 with " + x);
        }
    }
}

By "more flexible" I mean that you can pass the dictionary around, should you want to map the actions in one place and use it somewhere else.

(This uses C# 6 syntax to initialise the dictionary.)

Having said all that, it seems to me an unneccessary complication UNLESS you want to pass the dictionary around. If you don't, then just using an if/else cascade would probably be the best approach.

(Note: This answer is similar to Mike Corcoran's - I was writing it at the same time he was his, it seems. I'll leave it here since it takes a slightly different approach.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM