【问题标题】:Should an exception be thrown and handled if it cannot happen?如果不能发生异常是否应该抛出并处理?
【发布时间】:2017-08-27 13:52:02
【问题描述】:

我有一个函数,它接受三个参数(日、月和年)并创建一个新的 dateTime。这是一个公共函数,当下拉三个组合框中的任何一个时会调用它。

在单元测试时,我不小心输入了一个无效值并引发了一个无效的日期时间异常,但这在应用程序中是不可能的,因为组合框仅预先填充了有效值。

所以问题是我还应该在函数中检查和处理这个异常吗?

【问题讨论】:

  • 我见过太多的不可能的情况发生。
  • 我没听懂你说的单元测试时我不小心输入了一个无效值如果组合框预填充了有效值,你是如何输入无效值的仅值 ?
  • 这就是让我担心的原因!只是想知道什么是最佳实践,而不是不必要的过于复杂的方法
  • @Youssef13 我正在使用 MVP 模式,因此在应用程序中,视图控件用于调用演示者,但在我的单元测试中,我直接调用演示者公共方法。
  • @user5467760,他说当单元测试时我不小心输入了无效值。

标签: c# validation exception-handling


【解决方案1】:

一般来说,是的,任何公共函数都可以从任何地方调用,如果在某个时间点您确定谁将输入提供给函数,那么最好保护您的代码免受无效输入的影响。

但是,如果输入不好,这个假定的函数可以自行处理不可能的情况,而不会触发异常。

检查输入并遵循众所周知的 TryParse 模式相对容易

 public bool TryMakeDateTime(int year, int month, int day, out DateTime date)
 {
      date = DateTime.MinValue;

      if(!IsValidDay(year, month, day))
        return false;

      date = new DateTime(year, month, day);
      return true;
 }

 public bool IsValidDay(int year, int month, int day)
 {
     if(day < 1 || day > 31)
        return false;

     if(month < 1 || month > 12)
        return false;

     if(day > 30 && (month == 2 ||
                     month == 4 || 
                     month == 6 || 
                     month == 9 || 
                     month == 11))
        return false;

     // This is arbitrary, adjust the check to your constraints
     if(year < 1900 || year > 2099)
         return false;

     if(month == 2)
     {
         // IsLeapYear cannot handle values below 1 or higher than 9999
         // but we have already checked the year with more retrictive
         // constraints.
         int extraDay = (DateTime.IsLeapYear(year) ? 1 : 0);
         if(day > (28 + extraDay))
             return false;
     }
     return true;
 }

【讨论】:

  • 抱歉,但现在我有一台 PC 可用于测试动态编写的代码,并且我已经修复了此代码的第一个版本的一些问题。
【解决方案2】:

是的,如果该函数单独允许提交无效输入,则该函数应该为无效输入抛出异常。您不知道未来的开发人员如何或从何处调用此函数。但另一个更好的选择是对函数进行编码,以便只允许有效输入。

您可以通过将输入的类型从整数值更改为枚举来做到这一点。创建月份枚举

public enum CalendarMonth {
  NotSet = 0, January = 1, February = 2,
  March = 3, April = 4, May = 5, June = 6,
  July = 7, August = 8, September = 9,
  October = 10, November = 11, December = 12}

还有一个 DayOfMonth 枚举

public enum DayOfMonth {
  NotSet = 0, dom1 = 1, dom2 = 2, ....etc., ... dom31 = 31 }

您可以编写函数将只有 30 天的月份的第 31 天视为下个月的第一天,将 2 月 29 日、30 日和 31 日视为 3 月 1 日、2 日、3 日等,以避免处理这种情况无效。那么你的函数的签名将是

public DateTime NewDate(DayOfMonth dom, CalendarMonth month, int year);

并且不可能传递无效值。 (除了我猜DateIme.MinDateDateTime.MaxDate 范围之外的年份值)

【讨论】:

    【解决方案3】:

    您不应阻止或捕获异常。但是您应该确保在那种“不可能”的情况下确实会发生异常。

    例外适用于被认为不会“正常”发生的“不可能”情况。

    例如,如果您调用 DateTime 构造函数重载,则如果输入无效,该构造函数将已经抛出异常。如果您认为在您的情况下不会发生这种情况,请不要处理这种情况。框架生成的异常信息就好了。

    【讨论】:

      猜你喜欢
      • 2016-08-14
      • 2012-06-28
      • 1970-01-01
      • 1970-01-01
      • 2013-02-07
      • 2011-08-18
      • 2010-11-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多