【问题标题】:Nested switch? Or other solution checking different conditions?嵌套开关?或其他解决方案检查不同的条件?
【发布时间】:2012-09-04 12:59:47
【问题描述】:

好的,情况如下:

我有一个客户端,它有 2 个设置:ConnectionState 和 ConnectionSollState,都是同一个可枚举(TypeConnectionState),它们存储客户端连接的实际状态和连接应该是的状态。在 to 的每种组合上,都会发生不同的事情,例如 ConnectionState 为“已连接”但 ConnectionSollState 为“已关闭”-> 拆除客户端。所以我有 4 种可能性需要检查。 现在每个客户端都可以处理无限个会话,每个会话也有一个状态(StreamState & StreamSollState),这些状态可以有6个可枚举的选项。

从现在开始,我要创建大约 20 个开关条件,而我的代码看起来非常混乱,我在编码时每 5 分钟就会出错一次。有没有更简单的方法来处理这种情况? (if/else) 会让事情变得更糟。

例子:

private void RTSPWorker() {
    try {
        byte[] buffer = new byte[2048];
        while (!mb_RTSPWorkerAbbort) {
        // Call TransportWD
            Thread.Sleep(100 * mi_ConnectionTimeOut);

            // Check ConnectionSollState
            switch(ConnectionSollState) {
                case TypeConnectionState.Connected:
                    // ConnectionSollState = Connected, check ConnectionState
                    switch(ConnectionState) {
                        case TypeConnectionState.Connected:
                            // ConnectionState is connected, keep-alive!
                            if(GET_PARAMETER() == null) {
                                DESCRIBE();
                            }
                            // Check streams too
                            foreach (cRTSPStream oStream in mo_StreamDict.Values) {
                                // Check StreamSollState
                                switch(oStream.RTSPStreamSollState) {
                                    case cRTSPStream.TypeRTSPStreamState.Play:
                                        // SollState is PLAY, check State
                                        switch(oStream.RTSPStreamState) {
                                            case cRTSPStream.TypeRTSPStreamState.Play:
                                                //Stream is alive, keep-alive!
                                                if (oStream.PLAY() == null) { oStream.DESCRIBE(); } break;
                                            case cRTSPStream.TypeRTSPStreamState.Closed:
                                                // Reinitialise.
                                                if (oStream.SETUP() != null) { oStream.PLAY(); } break;
                                            default:
                                                // Default, send play.
                                                oStream.PLAY(); break;
                                        }
                                    break;
                                case cRTSPStream.TypeRTSPStreamState.Pause:
                                    // SollState is on pause, check State
                                    switch(oStream.RTSPStreamState) {
                                        case cRTSPStream.TypeRTSPStreamState.Closed:
                                            // Reinitialise.
                                            if (oStream.SETUP() != null) { oStream.PLAY(); } break;
                                        default:
                                            oStream.PAUSE();
                                            break;
                                    }
                                break;
                                case cRTSPStream.TypeRTSPStreamState.Closed:
                                    // SollState is closed, check State
                                    switch(oStream.RTSPStreamState) {
                                        case cRTSPStream.TypeRTSPStreamState.Closed:
                                            // Is closed, do nothing
                                            break;
                                        default:
                                            // Default teardown, remove session
                                            oStream.TEARDOWN();
                                            this.RemoveRTSPSession(oStream);
                                            break;
                                        }
                                default:
                                    // Default, what do?
                                    break;
                                }
                            }
                            break;
                        case TypeConnectionState.Closed:
                            // ConnectionState should be connected, re-connect!
                                while(Connect() != true) {
                                    // Sleep for 200ms, try again
                                    Thread.Sleep(200);
                                }
                            break;
                        default:
                            // TODO anything else
                            break;
                    }
                    break;
                case TypeConnectionState.Closed:
                    // Check ConnectionState
                    switch(ConnectionState) {
                        case TypeConnectionState.Connected:
                            // Is connected, should be closed. Close connection & clean up!
                            Close(null);
                            break;
                        default:
                            // Anything other than Connected, do nothing.
                            break;
                    }
                    break;

                default:
                    break;
            }
        }
    } catch {

    }
}

【问题讨论】:

  • 为什么不把某些东西移到更小的函数中呢?这样就可以解决问题了。
  • 如果你在 switch 语句中只有 Connected 检查,你应该使用 if 语句。否则样板代码太多。

标签: c# if-statement coding-style switch-statement case-statement


【解决方案1】:

天哪,蝙蝠侠,那是你那里的一些难以理解的代码。这里有一些提示可能会改善您的情况。我必须补充一点,这些提示有些偏颇,因为它们更喜欢个人编码风格:

  1. 将 switch 语句重写为 IF 语句。嵌套 IF 语句比嵌套的 switch 语句更容易阅读,即使您必须编写更多代码才能获得相同的结果。
  2. 将您的方法分解为几个子方法。您的方法可能试图同时做太多事情。 -> Seperation of Concerns & Single Responsibility Principle
  3. 使用改进的真值表。在您有基于变量组合产生某些结果的逻辑的情况下,您可能需要使用真值表结构。例如,使用 List<Tuple<T1,T2,T etc..>> 构造。

例如:

var truthTable = List<Tuple<NavigationTab, Role, Action>>
{
    new Tuple<NavigationTab, Role, Action>(NavigationTab.Users, Role.UsersAdministration, MVC.Administration.Users.Index()),
    new Tuple<NavigationTab, Role, Action>(NavigationTab.Users, Role.RolesAdministration, MVC.Administration.Roles.Index()),
    new Tuple<NavigationTab, Role, Action>(NavigationTab.Products, Role.ProductsAdministration, MVC.Administration.Products.Index()),
}

【讨论】:

  • 这是天才。我不知道。谢谢,先生!
  • 我受到 if 语句比 switch 语句慢的影响,这意味着在嵌套的情况下,您可能会损失相当多的性能(取决于 switch / if 的复杂程度和当然有多少嵌套级别)
  • 非常正确,但这是纳秒级的性能差异。在 99.9% 的现实生活场景中,这种水平的性能差异绝对可以忽略不计。相反,代码易读性和可维护性等问题更为重要。
【解决方案2】:

更好的面向对象通常被认为是 switch 语句的最佳替代方案。例如,您可以使用多态性而不是 switch 语句并执行以下操作:

try {
    byte[] buffer = new byte[2048];
    while (!mb_RTSPWorkerAbbort) {
        Thread.Sleep(100 * mi_ConnectionTimeOut);
        worker.DoWork();
    }
 } catch (Exception ex) {
     Trace.WriteLine(ex);
 }

随着状态的变化,您可以实例化不同类型的工作类,它们以不同的方式实现DoWork。一种可能是:

while(Connect() != true) {
    // Sleep for 200ms, try again
    Thread.Sleep(200);
}

当然,我不一定认为您应该那样做。我唯一需要了解的是您发布的代码 - 所以,可能有很多更好的方法来实现不同的行为。

这与Specification Pattern 非常相似。

【讨论】:

    【解决方案3】:

    将您的代码分解成更小、更易于管理的部分。

    如果你提取你的case语句中的逻辑,主循环可以重写而根本没有开关,例如:

    while (!mb_RTSPWorkerAbbort)
    {
        // Call TransportWD
        Thread.Sleep(100*mi_ConnectionTimeOut);
    
        if (ConnectionSollState == TypeConnectionState.Closed)
        {
            CloseIfConnected();
        }
    
        if (ConnectionSollState == TypeConnectionState.Connected)
        {
            if (ConnectionState == TypeConnectionState.Connected)
            {
                KeepAlive();
                ProcessStreams(); // <--bad example name - rename to what it's actually doing.
            }
            else
            {
                Reconnect();
            }
        }
    }
    

    当你在一个月后看到这个时(或者在你需要审查的情况下),你会更容易看到发生了什么。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-20
      • 1970-01-01
      • 2019-12-07
      • 2021-05-12
      相关资源
      最近更新 更多