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