请考虑以下代码:
class Program
{
public static explicit operator long(Program x) { return 47; }
static int Main(string[] args)
{
var x = new Program();
Console.WriteLine((decimal) x);
}
}
令我惊讶的是,这会输出47
;换句话说,即使转换为explicit operator long
,也会调用decimal
。
C#规范中是否有明确说明会发生这种情况(如果是这样,究竟在哪里)或者这是否是我遗漏的其他一些规则的结果?
答案 0 :(得分:6)
我能想到的唯一解释是编译器足够聪明,可以实现有一个隐式运算符,它将long转换为十进制,它可以用来满足Program和decimal之间的显式转换,当程序只能转换为长。
编辑:我们在这里;数字类型之间的转换内置于语言规范中:
6.1.2隐式数字转换
隐式数字转换为:
·从sbyte到short,int,long,float,double或decimal。
·从byte到short,ushort,int,uint,long,ulong,float, 加倍或小数。
·从short到int,long,float,double或decimal。
·从ushort到int,uint,long,ulong,float,double或 小数。
·从int到long,float,double或decimal。
·从uint到long,ulong,float,double或decimal。
·从long到float,double或decimal。
·从ulong到float,double或decimal。
·从char到ushort,int,uint,long,ulong,float,double, 或小数。
·从浮动到双倍。
从int,uint,long或ulong到float和from long或者的转换 ulong to double可能会导致精度损失,但永远不会导致 损失程度。其他隐式数字转换永远不会丢失 任何信息。
没有对char类型的隐式转换,所以。的值 其他整数类型不会自动转换为char类型。
因此,在程序和十进制之间进行转换时,C#知道它可以隐式地从任何数字类型转换为十进制,因此在执行此显式转换时,它将查找可以将程序转换为数字类型的任何运算符。
有趣的是,如果您还将显式转换为uint,那么会返回48会发生什么?编译器选择哪一个?
答案 1 :(得分:6)
我找到了答案。首先,有一种类型的概念被另一种包含,在 6.4.3评估用户定义的转换中定义如下:
如果存在从类型A到a的标准隐式转换(第6.3.1节) 类型B,如果A和B都不是接口类型,则说A 被包含在 B中,B被称为包含 A。
6.3.1标准隐式转换声明“隐式数字转换(第6.1.2节)”是标准隐式转换, 6.1.2隐式数字转换 turn定义了从long
到decimal
的隐式转换。因此,long
包含在 decimal
中。
接下来, 6.4.5用户定义的显式转换指出确定显式转换是否适用的其中一个阶段是:
查找适用的用户定义和提升转换集 运算符,U。此集由用户定义和提升组成 类或语句声明的隐式或显式转换运算符 D中的结构转换自S包含或包含的类型 到T包含或包含的类型。如果U是空的,那么 转换未定义,发生编译时错误。
此处,D
指的是较早步骤的结果,在这种情况下,仅包含decimal
,Program
和object
。因此,集合U
将包含我声明的Program
- 到 - long
显式运算符,因为long
包含decimal
(如前所述)。
接下来的步骤之一是选择long
作为最具体的目标类型, TX
。
最后,同一算法的最后一步说明:
最后,应用转换:
- 如果S不是SX,则执行从S到SX的标准显式转换。
- 调用最具体的用户定义转换运算符以将SX转换为TX。
- 如果TX不是T,则执行从TX到T的标准显式转换。
此处S
和SX
都是Program
,因此第一部分什么都不做。 TX
被选为long
而T
是目标类型decimal
,因此最后一部分执行从long
到{{1}的标准转换}。