2

让我给你举个例子。我的 aspx.cs 文件中有以下 Web 方法,用于 AJAX 调用:

[WebMethod]
public static ResponseMessage GetNextQuestion(string quizGuid)
{
    using (DbEntities db = new DbEntities())
    {
        Quiz theQuiz = Quiz.Get(db, DataValidationHelper.GetGuid(quizGuid));

        try
        {
            Question nextQuestion = QuizHelper.GetNextQuestion(db, theQuiz);

            return new ResponseMessage() { Status = "Success", NextQuestion = new NextQuestionResponse(nextQuestion, theQuiz) };
        }
        catch (QuizNotFoundException)
        {
            return new ResponseMessage() { Status = "QuizNotFound" };
        }
        catch (QuizInvalidException)
        {
            return new ResponseMessage() { Status = "QuizInvalid" };
        }
        catch (QuizOverException)
        {
            return new ResponseMessage() { Status = "QuizOver" };
        }
        catch (QuestionTimedOutException)
        {
            return new ResponseMessage() { Status = "QuestionTimedOut" };
        }
        catch (Exception ex)
        {
            return new ResponseMessage() { Status = "Error", ErrorMessage = ex.Message };
        }
    }
}

QuizHelper.GetNextQuestion方法从数据库生成一个新问题,在某些特定情况下会引发以下异常:

  1. QuizNotFoundException:当quizGuid在数据库中找不到给定的测验时。
  2. QuizInvalidException: 出于安全目的而抛出,例如当有人试图破解 HTTP 请求时。
  3. QuizOverException:每个测验有 10 个问题,当用户尝试使用该QuizHelper.GetNextQuestion方法获得第 11 个问题时,会抛出此异常。
  4. QuestionTimedOutException: 你必须在给定的时间内回答一个问题。如果不这样做,则会引发此异常。
  5. Exception:所有其他例外情况都在此分组,其唯一目的是通知用户发生错误,用于 UX 目的。

然后在 Javascript 文件中,ResponseMessage.Status检查并采取相应的措施。

我知道在此代码中使用异常来控制流程是不好的,但是以这种方式进行操作更直观且更简单。更不用说代码对于外人来说更容易理解的事实。

我不确定如何以“正确的方式”毫无例外地重写此代码,但同时保持其简单性。

我错过了什么,有什么想法吗?

更新:一些答案建议使用枚举来返回操作的状态,但我有很多操作,它们都可能导致不同的场景(即我不能对所有操作使用相同的枚举)。在这种情况下,每个操作创建一个 Enum 感觉不是正确的方法。对这个模型有什么改进吗?

4

4 回答 4

1

抛出和处理异常是昂贵的。当传递无效参数或对象即将进入无效状态等时,您可以随意抛出异常。在这里,您似乎在常规程序流中使用异常。

Microsoft 建议不要使用异常来改变程序流程。

虽然使用异常处理程序来捕获错误和其他中断程序执行的事件是一种很好的做法,但将异常处理程序用作常规程序执行逻辑的一部分可能会很昂贵,应该避免使用。在大多数情况下,应仅在不经常发生且不期望出现的情况下使用异常。作为典型程序流的一部分,不应使用异常来返回值。在许多情况下,您可以通过验证值和使用条件逻辑停止执行导致问题的语句来避免引发异常。

这是一个代码分析规则,它说明了这一点。

因此,在您的情况下,您需要返回某种ErrorCodeasEnumint。如果你觉得你需要在这里抛出一个异常,会ErrorCode更好或包裹你!QuizException

编辑:根据您的编辑,我认为您可以做到这一点。为什么无法创建 Enum 或 Int 作为错误代码?举个例子,WindowsSocket它暴露了SocketErrorCodes包含所有类型的套接字错误。或者更合适的一个是操作系统本身使用可以包装在的唯一SystemErrorCodesEnum不是吗?

如果我错了或错过了你的观点,请纠正我!

于 2013-08-29T20:55:47.623 回答
1

例如,您将使用枚举Quiz.Status并更改

Question nextQuestion = QuizHelper.GetNextQuestion(db, theQuiz)

Question nextQuestion;
Quiz.Status status = QuizHelper.TryGetNextQuestion(db, theQuiz, out nextQuestion);
switch(status) {
    case QuizNotFoundException:
        return new ResponseMessage() { Status = "QuizNotFound" };
    // ...
}

甚至简化为

Question nextQuestion;
Status status = QuizHelper.TryGetNextQuestion(db, theQuiz, out nextQuestion);
if(status != Status.ok) {
    return new ResponseMessage() (Status = status);
}

避免不同情况的长级联。

于 2013-08-29T20:37:24.790 回答
0

当引发异常时,这是“昂贵的”,因为在引发异常时会进行多次调用。

相反,我会推荐一个Question.StatusQuiz.Status它是一个枚举。

编辑:欲了解更多信息为什么尝试块昂贵

于 2013-08-29T20:36:54.290 回答
0

这里没有任何需要通过异常来管理流(可能是 ExceptionandTimeOutException除外)。请记住,异常本身就是一个足够重的工件,不适合流体控制流,而且顾名思义:它适用于异常情况。

有些地方您根本无法避免基于异常的流程,例如:当您使用设备时。这是基于从设备或 IO 收到的异常的非常常见的命令行为。

但是,正如我所说,这似乎不是你的情况,所以if/else只要有可能,只要用 simple 处理它。

于 2013-08-29T20:36:47.760 回答