11

我从另一个问题(稍作修改)中借用了下面的代码,以在我的代码中使用:

internal class PositiveDouble 
{
      private double _value;
      public PositiveDouble(double val) 
      {
          if (val < 0)
              throw new ArgumentOutOfRangeException("Value needs to be positive");
          _value = val;
      }

      // This conversion is safe, we can make it implicit
      public static implicit operator double(PositiveDouble d)
      {
          return d._value;
      }
      // This conversion is not always safe, so we're supposed to make it explicit
      public static explicit operator PositiveDouble(double d)
      {
          return new PositiveDouble(d); // this constructor might throw exception
      }
}

此代码的原作者正确地遵守了 MSDN 的隐式显式文档中给出的警告,但这是我的问题:在潜在异常代码中是否总是必要的explicit

所以,我的代码中有一些从 PositiveDouble 派生的类型(例如“Volume”),我希望能够像下面的第一行一样方便地设置实例:

Volume v = 10; //only allowed by implicit conversion
Volume v = new Volume(10)  //required by explicit conversion, but gets messy quick

被迫在任何地方使用显式强制转换会使代码的可读性大大降低。它如何保护用户?在我的程序的语义中,我从不期望 Volume 是负数。事实上,如果它发生了,我希望会抛出一个异常。因此,如果我使用隐式转换并抛出异常,什么“意外结果”可能会让我大吃一惊?

4

2 回答 2

14

C# 语言规范在 10.10.3 转换运算符下说:

如果用户定义的转换可能导致异常(例如,因为源参数超出范围)或信息丢失(例如丢弃高位),则该转换定义为显式转换。

此外,来自MSDN:implicit (C# Reference)

一般来说,隐式转换运算符不应该抛出异常,也不应该丢失信息,以便在程序员不知情的情况下安全地使用它们。如果转换运算符不能满足这些条件,则应将其标记为显式。

考虑到这一点,你operator PositiveDouble(double d) 不应该被标记implicit,因为Volume v = -1会抛出异常。

所以回答你的问题:

在潜在的异常代码中是否总是需要显式?

不,这不是必需的,但它应该

基于意见的边界:如果您的代码是使用此转换的唯一代码,并且您发现隐式转换更易于阅读和/或维护,请随意使用它。

至于

它如何保护用户?

请参阅MSDN:显式(C# 参考)提及:

如果转换操作可能导致异常或丢失信息,则应将其标记为显式。这可以防止编译器静默调用转换操作而产生可能无法预料的后果。

我无法真正理解何时会发生这种情况,但同样,如果您认为您永远不会在任何地方的代码中从负双精度转换,那么这不应该抛出。

于 2014-09-25T19:44:24.067 回答
13

只有当它们总是成功时,转换才应该是隐式的。如果它们可能失败或导致信息丢失,它们应该是显式的,因为这需要转换操作的调用者有意表明他们想要这种转换,因此准备好处理结果,无论结果如何。

我们可以在框架原始数字类型中看到这一点;将 a分配int给 along是一种隐式转换,因为它总是会成功获得预期的结果。采取另一种方式可能会导致OverflowException检查上下文,并可能导致未检查上下文中的截断(信息丢失),因此您需要通过显式转换来表明您打算进行此转换。

于 2014-09-25T19:44:14.643 回答