【发布时间】:2008-12-23 21:28:29
【问题描述】:
关于何时可以、应该或不应该定义用户定义的隐式转换的一般准则是什么?
我的意思是,例如,“隐式转换不应丢失信息”、“隐式转换不应抛出异常”或“隐式转换不应实例化新对象”。我很确定第一个是正确的,第三个不是(或者我们只能隐式转换为结构),我不知道第二个。
【问题讨论】:
关于何时可以、应该或不应该定义用户定义的隐式转换的一般准则是什么?
我的意思是,例如,“隐式转换不应丢失信息”、“隐式转换不应抛出异常”或“隐式转换不应实例化新对象”。我很确定第一个是正确的,第三个不是(或者我们只能隐式转换为结构),我不知道第二个。
【问题讨论】:
第一个并不像您想象的那么简单。这是一个例子:
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);
}
}
就我个人而言,我很少创建自己的隐式转换。我对框架中的那些感到很满意,但我很少觉得添加我自己的会让生活变得更好。 (顺便说一句,一般的值类型也是如此。)
编辑:只是为了稍微回答一下这个问题,可能值得阅读conversion operators part of the Microsoft class library design guidelines。
【讨论】:
firstDouble==secondDouble.我认为“令人惊讶”的是firstLong == secondDouble 在没有诊断的情况下编译并返回 true。对firstDouble 和secondDouble 的分配不能指望做任何事情,除了使firstDouble 持有firstLong 和secondLong 的最佳可用double 表示。相比之下,由于== 有一个重载,它的第一个运算符类型需要转换,而一个不需要转换的,我更喜欢那里的编译器诊断。
我绝对同意第一个 - 大多数时候是第二个(“永不言败”),但不会对第三个感到兴奋;除此之外,它在结构和类之间创建了不必要的区别。在某些情况下,隐式转换可以极大地简化调用约定。
对于显式转换,一切皆有可能。
不过,您并不经常需要编写转换运算符。在许多情况下,显式运算符更有用,如果条件不正确,它们可以中断(例如,Nullable<T>,如果它们没有值)。
【讨论】:
如果您只支持您自己类型的对象之间的隐式转换,我建议您决定应该将哪些“公理”应用于此类转换(例如,如果a==b、@ 987654323@ 和 a==c 都是合法的,其中两个为真,第三个也必须为真)然后确定一组最有用的转换,这些转换将使这些公理成立。我在我的博客http://supercatnet.blogspot.com/2013/09/axioms-of-implicit-type-conversion.html 上讨论了四个有用的公理(不幸的是,.NET Framework 包含违反所有四个的转换,而不允许不必要的转换)。
需要考虑的重要一点是,在大多数情况下,隐式转换到不精确类型比转换来自不精确类型造成的意外风险要小,但有一个值得注意的地方例外:如果将更精确类型的某些东西提供给具有重载的方法或运算符,该重载采用更精确的类型和可以转换为的不太精确的类型,则某种程度的“惊人”行为将是除非可以使隐式转换成为不可能,否则几乎是不可避免的[例如如果程序员编写 if (someLong == someFloat) 的行为可能令人惊讶,但这并不是因为隐式 long 到 float 转换的精度损失令人惊讶,而是因为至少有六种不同的方式可以比较 @ 987654328@ 和 float(*) 以及 any 意味着编译器可能会附加到直接比较上,这会让那些期待其他结果的人感到惊讶。我知道避免这种惊讶的唯一解决方案是提供重载以明确涵盖所有模棱两可的情况,并用[Obsolete()] 标签标记它们。这样做可能会有些尴尬,但会提供能够满足所有四个公理的实质性优势。
(*) 程序员可能有意测试 long 是否等于四舍五入到最接近的浮点值,截断为零,或向下无穷大;或者,程序员可能想要测试float 是否代表long,float 和long 是否具有相同的标称值,或者float 和long 是否会两者都转换为相同的double。
【讨论】:
导致我提出这个问题的临界案例违反了第二个问题。也就是说,我有一个 Lazy
【讨论】: