关于何时可以、应该或不应该定义用户定义的隐式转换的一般准则是什么?
我的意思是,例如,“隐式转换不应该丢失信息”、“隐式转换不应该抛出异常”或“隐式转换不应该实例化新对象”。我很确定第一个是正确的,第三个不是(或者我们只能隐式转换为结构),我不知道第二个。
第一个并不像你想象的那么简单。这是一个例子:
using System;
class Test
{
static void Main()
{
long firstLong = long.MaxValue - 2;
long secondLong = firstLong - 1;
double firstDouble = firstLong;
double secondDouble = secondLong;
// Prints False as expected
Console.WriteLine(firstLong == secondLong);
// Prints True!
Console.WriteLine(firstDouble == secondDouble);
}
}
就我个人而言,我很少创建自己的隐式转换。我对框架中的那些感到很满意,但我很少觉得添加我自己的会让生活变得更好。(顺便说一句,一般的值类型也是如此。)
编辑:只是为了稍微回答一下这个问题,可能值得阅读Microsoft 类库设计指南中的转换运算符部分。
我绝对同意第一个 - 大多数时候是第二个(“永不言败”),但不会对第三个感到兴奋;除此之外,它在结构和类之间创建了不必要的区别。在某些情况下,隐式转换可以极大地简化调用约定。
对于显式转换,一切皆有可能。
不过,您并不经常需要编写转换运算符。而且在许多情况下,显式运算符更有用,如果条件不正确(例如,Nullable<T>
如果它们没有值,它们可以中断)。
如果您只支持您自己类型的对象之间的隐式转换,我建议您决定应该将哪些“公理”应用于此类转换(例如,确定 if a==b
、b==c
和a==c
都是合法的并且两个其中一个是真的,第三个也必须是真的)然后确定一组最有用的转换,这些转换将使这些公理成立。我在我的博客http://supercatnet.blogspot.com/2013/09/axioms-of-implicit-type-conversion.html上讨论了四个有用的公理(不幸的是,.NET Framework 包含违反所有四个的转换,而不允许不必进行的转换)。
需要考虑的重要一点是,在大多数情况下,与从不精确类型的转换相比,到不精确类型的隐式转换带来的意外风险要小,但有一个明显的例外:如果将更精确类型的某些东西提供给方法或运算符它具有可以转换为更精确类型和不太精确类型的重载,除非可以使隐式转换成为不可能,否则某种程度的“惊人”行为几乎是不可避免的[例如,如果程序员编写的行为可能令人惊讶,但不是因为隐式转换的精度损失令人惊讶,而是因为至少有六种不同的方式可以比较if (someLong == someFloat)
long
float
long
和float
(*),以及编译器可能附加到直接比较的任何含义都会让那些期待其他东西的人感到惊讶。我知道避免这种惊讶的唯一解决方案是提供重载以明确涵盖所有模棱两可的情况并用[Obsolete()]
标签标记它们。这样做可能会有些尴尬,但会提供能够满足所有四个公理的实质性优势。
(*) A programmer might plausibly be intending to test whether the long was equal to the value of the float rounded to nearest, truncated toward zero, or floored toward negative infinity; alternatively, the programmer might be wanting to test whether the float
was the one that represents the long
, whether the float
and long
have the same nominal value, or whether the float
and long
would both convert to the same double
.
导致我提出这个问题的边缘案例违反了第二个问题。也就是说,我有一个 Lazy<T> 类(不是每个人吗?)并开始思考我是否不应该提供到 T 的隐式转换。我的直觉是说是的,当前版本是,但我不是太当然。