为什么/何时将运算符指定为显式是重要的?

时间:2014-09-25 19:37:30

标签: c# implicit-conversion explicit-conversion

我从another question(稍加修改)借用了以下代码,以便在我的代码中使用:

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 implicit&amp;中提供的警告。 explicit文档,但这是我的问题: explicit是否始终需要特殊代码?

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

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

强制在任何地方使用显式强制转换都会使代码的可读性降低。它如何保护用户?在我的程序的语义中,我从不期望卷是负面的;事实上,如果它发生了,我希望抛出异常。因此,如果我使用隐式转换并抛出,“意外结果”可能会破坏我吗?

2 个答案:

答案 0 :(得分:14)

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

  

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

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

  

一般情况下,隐式转换运算符不应该抛出异常并且永远不会丢失信息,以便在没有程序员意识的情况下可以安全地使用它们。如果转换运算符不符合这些条件,则标记为显式。

考虑到这一点,您的operator PositiveDouble(double d) 不应标记为implicit,因为Volume v = -1会引发异常。

所以回答你的问题:

  

在潜在的特殊代码中是否总是需要显式?

不,没有必要,但应该

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

至于

  

它如何保护用户?

请参阅MSDN: explicit (C# Reference)提及:

  

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

当发生这种情况时,我无法理解,但是,如果你认为你的代码中的任何地方都没有从负双重转换,那么这不应该抛出。

答案 1 :(得分:13)

转换只有在始终成功时才应隐含。如果它们可能失败或导致信息丢失,它们应该是明确的,因为这需要转换操作的调用者故意表明他们想要这种转换,因此准备处理结果,无论可能是什么。

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