C# 为什么/何时将运算符指定为显式运算符很重要?

C# 为什么/何时将运算符指定为显式运算符很重要?,c#,implicit-conversion,explicit-conversion,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");

我借用了下面的代码(稍作修改),以便在我的代码中使用:

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
      }
}

强制到处使用显式强制转换会使代码的可读性大大降低。它如何保护用户?在我的程序语义中,我从不期望卷是负的;事实上,如果发生这种情况,我希望抛出一个异常。所以,如果我使用隐式转换,它抛出什么样的“意外结果”可能会让我大吃一惊呢?

只有在转换总是成功的情况下,转换才应该是隐式的。如果它们可能会失败或导致信息丢失,那么它们应该是显式的,因为这需要转换操作的调用方有意地指示它们希望进行此转换,因此准备处理结果,不管结果如何

我们可以在框架原语数字类型中看到这一点;将
int
赋值给
long
是一种隐式转换,因为它总是会成功地得到预期的结果。相反,在选中的上下文中可能会导致溢出异常,在未选中的上下文中可能会导致截断(信息丢失),因此您需要通过显式强制转换来表明您打算进行此转换。

C语言规范在10.10.3转换运算符中规定:

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

此外,来自:

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

考虑到这一点,您的
运算符正双倍(双d)
不应被标记为
隐式
,因为
卷v=-1
将引发异常

所以要回答你的问题:

在潜在的异常代码中,显式总是必要的吗

不,没有必要,但它应该是

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

至于

它如何保护用户

见提及:

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


我真的不知道什么时候会发生这种情况,但是,再一次,如果你认为你从来没有在你的代码中转换过负双精度,这就不应该抛出

在我看来,将转换运算符更改为隐式应该没问题。建议这样做,尤其是在公共API中。它防止人们在没有注意到它可能抛出的情况下使用cast,从而节省调试时间。让人们注意演员阵容可以避免麻烦。他们说“哦,那么你认为你很聪明,让所有的演员都变得含蓄?”你说“如果你知道你在做什么,你就不会有问题”——想象一个炉子在有东西在上面时启动,因为“每次被迫启动炉子会使烹饪效率大大降低”,而制造商对这种说法的回应是“那就不要把东西放在上面”——只是不那么引人注目,没有灼伤。“我真的不知道什么时候会发生这种情况”——当将带有自定义转换的对象传递给接受这种参数的框架方法时,这种情况很容易发生。除非你(a)确切知道参数类型,否则在代码中这种情况并不明显框架方法接受,并且(b)知道隐式转换存在。
Volume v = 10; //only allowed by implicit conversion
Volume v = new Volume(10)  //required by explicit conversion, but gets messy quick