与非常数切换的替代方案

问题描述 投票:0回答:5

我刚刚了解到 switch 语句不能使用非常量条件。这很好,我明白了。但这真的意味着我必须制作一个大的 if-else 块吗?太丑了,我都哭了

一些背景:我正在做一个Unity项目,我想打开当前的动画状态。检查当前动画状态的一个好方法是比较哈希值,这意味着我需要计算动画状态的哈希值。计算完它们后我想打开它们。 (写这篇文章时我意识到我可以将生成的哈希值粘贴到一个常量中,但现在我仍然想要一个答案)

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

最好的方法是什么?

c# switch-statement
5个回答
7
投票

你可以用字典来做到这一点。

试试这个:

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`

为了让它更干净一点,你可以定义一个

Switch
类,如下所示:

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

那么代码如下所示:

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

如果你这样写,它看起来几乎就像一个正常的

switch
声明:

var @switch = new Switch()
{
    {
        () => hash == state1,
        () =>
        {
            /* Do stuff */
        }
    },
    {
        () => hash == state2,
        () =>
        {
            /* Do other stuff */
        }
    },
};

4
投票

您还可以使用 caseguardslocalfunctions,如下所示:

bool HashMatches(int TargetHash) => hash == TargetHash;

switch (true):
{
case true when HashMatches(state1):
    //DoStuff
    break;
case true when HashMatches(state2):
    //Other stuff
    break;
}

1
投票

能否简化,取决于你的“DoStuff”、“Other Stuff”、“Next Stuff”、“You other stuff”之间的相似度

  1. 假设您的

    Stuff
    “家庭成员”实际上是:

    int stuffAction(int state){
        int modified_state;
        //do something on state and modified state
        return modified_state;
    }
    

    那么,显然你

    Stuffs
    可以通过使用function来简化,如上所示。只要你的
    Stuff
    具有相同的函数和不同的参数,它就可以同样简化。

  2. 此外,如果您

    Stuffs
    的形式不同,但具有相同的输入参数,则可以创建
    Dictionary
    delegates
    (参见
    System.Collections.Generic.Dictionary<string, System.Delegate>
    ),这样当您可以调用
    Stuff
    时,您只需需要做

    dic[state](input parameters here)
    

    而不是使用 if-else 或 switch

在某些情况下,您的代码可能无法进一步简化,但正如我之前所说,底线取决于您的

Stuff
之间的相似性。


0
投票

你只能用 if-else if 来做到这一点:

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

0
投票

您可以使用条件运算符来完成伪 switch 语句,尽管它实际上是一个 if-else 块。这假设您的“do stuff”部分可以包装在返回通用类型的方法中。假设

DoState1
DoState2
都返回
void
...

hash == state1 ? DoState1()
: hash == state2 ? DoState2()
: throw new NotImplementedException();

如果您想返回一个值...更改 Do 方法以返回相同类型(例如 bool)并将其分配给变量。

bool result 
  = hash == state1 ? DoState1()
  : hash == state2 ? DoState2()
  : false;

它的感觉和读起来都非常像 switch 语句或表达式。唯一的限制是您必须将代码块封装到方法中,无论如何这是一个很好的做法。

此方法也非常适合 LINQ,因为它可以转换为 SQL CASE 语句(适用于 EFC7+ 和 SQL Server)

.Select(x => new Dto
{
   RecordExists = x.Source.RecordExists is null ? "UNKNOWN"
                : x.Source.RecordExists > 1 ? "MANY"
                : x.Source.RecordExists == 1 ? "ONE"
                : "NO"
});
© www.soinside.com 2019 - 2024. All rights reserved.