Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Better Alternative to Case Statement

I currently have a switch statement that runs around 300 odd lines. I know this is not as giant as it can get, but I'm sure there's a better way to handle this.

The switch statement takes an Enum that is used to determine certain properties that pertain to logging. Right now the problem sets in that it is very easy to leave out an enumeration value and that it will not be given a value as it is not in the switch statement.

Is there an option one can use to ensure that every enumeration is used and given a custom set of values it needs to do its job?

EDIT:


Code sample as requested: (This is simplistic, but shows exactly what I mean. Also an Enumeration would exist with the below values.)

internal void GenerateStatusLog(LogAction ActionToLog)
{
    switch (ActionToLog)
    {
        case LogAction.None:
            {
                return;
            }
        case LogAction.LogThis:
            {
                ActionText = "Logging this Information";
                LogText = "Go for it.";

                break;
            }
    }

    // .. Do everything else
}
like image 261
Kyle Rosendo Avatar asked May 21 '10 07:05

Kyle Rosendo


People also ask

What is alternative of CASE statement in this given program?

Question: What is Alternative to CASE Statement in SQL Server? Answer: IIF Function.

What is the alternative for CASE statement in Oracle?

The Oracle functions CASE, DECODE, and COALESCE all perform similar functionality. They can transform a value into another value.

Which is the correct format for CASE statement?

The CASE statement always goes in the SELECT clause. CASE must include the following components: WHEN , THEN , and END . ELSE is an optional component. You can make any conditional statement using any conditional operator (like WHERE ) between WHEN and THEN .

What are the types of the CASE statement?

The CASE statement chooses one sequence of statements to execute out of many possible sequences. The CASE statement has two types: simple CASE statement and searched CASE statement. Both types of the CASE statements support an optional ELSE clause.


2 Answers

EDIT

I thought this over again, looked around in related questions in SO, and I wrote some code. I created a class named AdvancedSwitch<T>, which allows you to add cases and exposes a method to evaluate a value and lets you specify values that it should check for existence.

This is what I came up with:

public class AdvancedSwitch<T> where T : struct
{
    protected Dictionary<T, Action> handlers = new Dictionary<T, Action>();

    public void AddHandler(T caseValue, Action action)
    {
        handlers.Add(caseValue, action);
    }

    public void RemoveHandler(T caseValue)
    {
        handlers.Remove(caseValue);
    }

    public void ExecuteHandler(T actualValue)
    {
        ExecuteHandler(actualValue, Enumerable.Empty<T>());
    }

    public void ExecuteHandler(T actualValue, IEnumerable<T> ensureExistence)
    {
        foreach (var val in ensureExistence)
            if (!handlers.ContainsKey(val))
                throw new InvalidOperationException("The case " + val.ToString() + " is not handled.");

        handlers[actualValue]();
    }
}

You can consume the class this way:

public enum TrafficColor { Red, Yellow, Green }

public static void Main()
{
    Console.WriteLine("Choose a traffic color: red, yellow, green?");
    var color = (TrafficColor)Enum.Parse(typeof(TrafficColor), Console.ReadLine());
    var result = string.Empty;

    // Creating the "switch"
    var mySwitch = new AdvancedSwitch<TrafficColor>();

    // Adding a single case
    mySwitch.AddHandler(TrafficColor.Green, delegate
    {
        result = "You may pass.";
    });

    // Adding multiple cases with the same action
    Action redAndYellowDelegate = delegate
    {
        result = "You may not pass.";
    };
    mySwitch.AddHandler(TrafficColor.Red, redAndYellowDelegate);
    mySwitch.AddHandler(TrafficColor.Yellow, redAndYellowDelegate);

    // Evaluating it
    mySwitch.ExecuteHandler(color, (TrafficColor[])Enum.GetValues(typeof(TrafficColor)));

    Console.WriteLine(result);
}

With the creative use of anonymous delegates, you can easily add new cases to your "switch block". :)
Not that you can also use lambda expressions, and lambda blocks, eg () => { ... } instead of delegate { ... }.

You can easily use this class instead of the long switch blocks.

Original post:

If you use Visual Studio, always create swich statements with the switch code snippet. Type switch press tab twice, and it auto-generates all the possibilities for you.

Then, add a default case to the end which throws an exception, that way when testing your app you will notice that there is an unhandled case, instantly.

I mean something like this:

switch (something)
{
    ...
    case YourEnum.SomeValue:
        ...
        break;
    default:
        throw new InvalidOperationException("Default case reached.");
}
like image 54
Venemo Avatar answered Oct 11 '22 12:10

Venemo


Well, there's throwing in the default case... There's no edit / compile time construct other than that.

However Strategy, Visitor and other patterns related to them may be appropriate if you choose to do it at run time.

Sample code will help with getting the best answer.

EDIT: Thanks for the sample. I still think it needs a bit of fleshing out as you dont cover whether there are some parameters that only apply to some cases etc.

Action is often used as an alias for the Command pattern and the fact that your Enum is called LogAction signifies that each value carries with it a behavior - be that implied (you stick appropriate code in a case) or explicit (in the specific Command hierarchy class).

Thus it looks to me like a usage of the Command pattern is appropriate (though your sample doesnt prove it) - i.e., have a class (potentially a hierarchy using constructor overloads or any other [set of] factory mechanisms) that keeps the state associated with the request along with the specific behaviour. Then, instead of passing an Enum value, create an appropriate LogCommand instance to the logger, which just invokes it (potentially passing a Log Sink 'receptacle' which the Command can log into). Otherwise you're poking random subsets of parameters in different places.

SEEALSO related posts:

  • C# - Is there a better alternative than this to ‘switch on type’?

  • Replace giant switch statement with what?

like image 21
Ruben Bartelink Avatar answered Oct 11 '22 14:10

Ruben Bartelink