C#数字字面量后缀

26

我刚接触C#,想要了解值(values)的工作原理。当我看一个普通的整数值时,它有三个重要的部分:类型、名称和值。

int testInt = 3;
 |    |       |
Type Name   Value

但是当我看到浮点数时,由于后缀F,会有一点困惑。

float testFloat = 3.0F;
  |      |        |  |
 Type   Name  Value Type

现在这里有两种类型,没有F后缀时,值将是双精度浮点型。但是当我可以声明双精度变量时,为什么会发生这种情况呢?

现在有两种类型,在没有F后缀的情况下,值将为双精度。但是,如果我可以用双精度变量声明,为什么会出现这种情况?

double testDouble = 3.0D;

第一个词是double,应该足够了,不是吗?对于带有后缀M的十进制值也是如此:

decimal testDecimal = 3.0M;

当涉及到其他后缀时,它开始让我感到非常困惑:

ulong bigOne = 2985825802805280508UL;

我以前在测试中使用过 ulong,知道 u 代表 "unsigned",可以让值比正常情况下高两倍。然后你再把 U 作为后缀,L 表示字面量(literal),就像 Google 所说的那样。据我理解,“字面量”是包含数字的值类型。但是我不明白的是,为什么即使没有后缀,这个 ulong 也能工作?

ulong bigOne = 2985825802805280508;

接着我尝试了不同的方法来理解后缀的重要性。

byte testLong = 12312UL;

这不起作用是因为该值对于字节(254)来说太高,后缀不能将其转换为长变量。

为什么第一个单词(类型)不足以进行声明?第一个单词应该足以告诉类型。始终给值加上后缀是最佳实践吗?


3
没错,当有赋值语句时,一个类型声明就足够了。这就是为什么在 C# 中添加了 var 关键字的原因:https://msdn.microsoft.com/zh-cn/library/bb383973.aspx - Max
3
U代表无符号(unsigned),而不是未赋值(unassigned)。 - Lee Taylor
1
还有像4.0 / 3这样的计算呢?应该使用单精度还是双精度?这真的会影响结果。 或者考虑2000000000 * 3呢?这是溢出还是只是一个长整型?你并不总是直接指定值。 - dryman
1
这些基本上是运行时转换,用于向编译器提供指示。1000 是一个 int,但如果你将其初始化为 long,你需要在末尾加上 L。你可以在这里找到更多信息:http://www.dotnetperls.com/suffix。 - Mihai-Daniel Virna
1
@user3772108 您声明创建一个 float 类型的变量并不能确定您赋值的数字字面量的类型,也不能改变其类型。 - Servy
显示剩余3条评论
3个回答

27

您在这里混淆了两件不同的事情:

float testFloat = 3.0F;

float 告诉编译器变量 testFloat 将是浮点值。 F 告诉编译器字面量 3.0 是一个 float。编译器需要了解这两个信息才能判断是否可以将该字面量赋给该变量,且无需转换或隐式转换。

例如,您可以执行以下操作:

float testFloat = 3;

没关系,编译器会把3看作整型字面量,但它知道可以将其赋值给浮点型而不会失去精度(这是隐式转换)。但如果您这样做:

float testFloat = 3.0;

3.0 是一个双精度字面量(因为默认情况下没有后缀),它不能隐式地(即自动地)将双精度数转换为单精度数,因为单精度数的精度更低。换句话说,信息可能会丢失。所以你要么告诉编译器它是一个单精度字面量:

float testFloat = 3.0f;

或者你可以通过使用一个 显式 强制转换来告诉它你可以接受任何精度损失:

float testFloat = (float)3.0;

4
没错。如果你执行了var result = 4 / 3;,编译器必须决定它是整数操作还是浮点数操作,这仅取决于赋值操作符右侧的内容。默认情况下,整数值被视为int,小数值被视为double。所以这将使得result成为一个值为1的int。如果你使用4 / 3.0代替,result将会是一个值为1.3333333的double - Andrew
现在我更好地理解了,谢谢。我从来没有在声明中进行过计算。我以为数字之间的点对于编译器来说足够理解了。 - user3772108
但即使有计算示例,开头的浮点数也足以表明我想要一个浮点数吗?因为只有后缀的Byte到Long转换不起作用? - user3772108
1
@user3772108:不,不是这样的。因为编译器在决定赋值是否有效之前需要知道左侧和右侧是什么。就像Servy的回答中所说,它无法根据左侧的上下文来确定右侧的类型。 - Matt Burland

10

所有表达式都需要能够解析到一个类型。因此,42这个表达式必须始终只有一个类型(它恰好是一个int)。如果你将它赋值给一个int变量,它就不能是一个double。无论在什么上下文中使用一个表达式,都不会决定它的解析类型。

这就是为什么数字字面量可以有后缀;这是一种在表达式中定义其类型的方式。

请注意,许多数值类型之间也存在隐式转换。例如,如果你写double d = 42;,那么42这个表达式实际上是一个整数,但是在赋值之前会执行一个隐式转换操作将其转换为一个double

有些特殊情况下有例外,比如lambda表达式,它的类型取决于它的使用方式,以及方法组,在没有上下文的情况下,这些表达式没有类型。


你的回答和Matt Burland的帮助让我更好地理解了。非常感谢你们。 - user3772108

2

有其他的方法可以在不指定变量类型的情况下声明一个变量:

var myNumber = 10;

在这种情况下,变量类型将由文字值定义。
如果您使用类型(double | float | int | ...)而不是“var”,则编译器会将文字值转换为变量类型(在可能的情况下)。
因此,我认为当您使用“var”声明变量且文字值类型不是默认关联的类型时,后缀很重要;
还有另一个原因使用后缀也非常有用,例如在您想要在表达式中进行隐式转换的情况下。

3
这是一种需要通过后缀来区分不同数字字面值的情况,但这并不是原因,而且这肯定不是唯一的这种情况(毕竟,在语言中添加var关键字是在几个版本之后,但自语言开始以来就存在后缀的需求)。 - Servy
那么我可以使用var和正确的后缀来声明每个变量吗? - user3772108
1
@user3772108 在大多数情况下是可以的,但有一些例外是不行的,比如定义一个 Func。但是大多数声明都可以用 var 解决,例如:var arrayOfInt = new [] {1,2,3}。不能使用的例子:Func<int,int,int> add = (a,b) => a+b,在这种情况下必须定义变量类型。您可以在此处了解更多关于 var 的信息 https://msdn.microsoft.com/zh-cn/library/bb383973.aspx - Alberto Monteiro
谢谢你提供的链接,Alberto Monteiro。我会去看一下。 - user3772108
1
是的,我可以这样做 var func = new Func<int,int,int>((a,b) => a+b)。 另一个语句也是有效的 Func<int,int,int> add = (a,b) => a+b。但是这个语句不可能实现 var func = (a,b) => a+b。所以就是这样。 - Alberto Monteiro
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接