Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Alternative to switch with non-constant

I just learned that a switch statement can't use non-constant conditions. Which is fine and all, I get it. But does that really mean I have to make a big if-else block? It's so ugly I'm crying.

Some context: I'm doing a Unity project and I want to switch on the current animation state. A good way to check the current animation state is to compare hashes, which means I need to calculate the hashes for the animation state. After calculating them I want to switch on them. (Writing this I realized I can paste the resulting hash into a constant, but now I still want an answer)

int state1 = Animator.StringToHash("State1");
int state2 = Animator.StringToHash("State2");
int hash = _myAnimator.GetCurrentAnimatorStateInfo(0).shortNameHash;
switch (hash):
{
case state1:
    //DoStuff
    break;
case state2:
    //Other stuff
    break;
}

What's the best way to do this?

like image 320
Ozitiho Avatar asked Mar 14 '23 20:03

Ozitiho


1 Answers

You can do this with a dictionary.

Try this:

int state1 = Animator.StringToHash("State1");
int state2 = Animator.StringToHash("State2");
int hash = _myAnimator.GetCurrentAnimatorStateInfo(0).shortNameHash;
var cases = new Dictionary<Func<bool>, Action>()
{
    { () => hash == state1, () => { /* Do stuff */} },
    { () => hash == state2, () => { /* Do other stuff */} },
};

cases
    .Where(c => c.Key()) // find conditions that match
    .Select(kvp => kvp.Value) //select the `Action`
    .FirstOrDefault() // take only the first one
    ?.Invoke(); // Invoke the action only if not `null`

To make it a little more clean you could define a Switch class like this:

public class Switch : IEnumerable<Switch.Case>
{
    private List<Case> _list = new List<Case>();

    public void Add(Func<bool> condition, Action action)
    {
        _list.Add(new Case(condition, action));
    }

    IEnumerator<Case> IEnumerable<Case>.GetEnumerator()
    {
        return _list.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return _list.GetEnumerator();
    }

    public void Execute()
    {
        this
            .Where(c => c.Condition())
            .Select(c => c.Action)
            .FirstOrDefault()
            ?.Invoke();
    }

    public sealed class Case
    {
        private readonly Func<bool> _condition;
        private readonly Action _action;

        public Func<bool> Condition { get { return _condition; } }
        public Action Action { get { return _action; } }

        public Case(Func<bool> condition, Action action)
        {
            _condition = condition;
            _action = action;
        }
    }
}

Then the code looks like this:

int state1 = Animator.StringToHash("State1");
int state2 = Animator.StringToHash("State2");
int hash = _myAnimator.GetCurrentAnimatorStateInfo(0).shortNameHash;
var @switch = new Switch()
{
    { () => hash == state1, () => { /* Do stuff */} },
    { () => hash == state2, () => { /* Do other stuff */} },
};

@switch.Execute();

And if you write it like this it almost looks like a normal switch statement:

var @switch = new Switch()
{
    {
        () => hash == state1,
        () =>
        {
            /* Do stuff */
        }
    },
    {
        () => hash == state2,
        () =>
        {
            /* Do other stuff */
        }
    },
};
like image 84
Enigmativity Avatar answered Mar 23 '23 08:03

Enigmativity