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

kmo*_*ote 11 c# implicit-conversion explicit-conversion

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

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
      }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

Cod*_*ter 14

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

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

另外,从MSDN:implicit(C#Reference):

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

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

所以回答你的问题:

在潜在的特殊代码中是否总是必需的?

不,这没有必要,但它应该.

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

至于

它如何保护用户?

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

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

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

  • *"当发生这种情况时我无法理解"* - 将带有自定义转换的对象传递给接受此类参数的框架方法时很容易发生.除非你(a)确切地知道框架方法接受什么参数类型,并且(b)知道隐式转换存在,否则代码中不会发生这种情况. (2认同)

cdh*_*wie 13

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

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