是否在编译时对 C# 中的文字进行算术运算?
Are arithmetic operations on literals in C# evaluated at compile time?
非常简短的问题,但我现在无法在网上找到解决方案。
int test = 1 + 2;
1 + 2
会在 运行 或编译时执行吗?
提问原因:
我认为大多数人有时会在不说明使用原因或含义的情况下使用文字,因为他们不想通过计算 运行 浪费一点性能,而且我相信计算是在编译期间发生的,没有任何效果关于性能:
int nbr = 31536000; //What the heck is that?
而不是
int nbr = 365 * 24 * 60 * 60; //I guess you know what nbr is supposed to be now...
由于您的示例本质上是常量表达式(即它们仅由常量或计算结果的结构组成),它们将 be evaluated at compile time.
A constant-expression is an expression that can be fully evaluated at
compile-time.
The type of a constantexpression can be one of the following: sbyte, byte, short, ushort,
int, uint, long, ulong, char, float, double, decimal, bool, string,
any enumeration type, or the null type.
常量表达式中允许以下结构:
- 文字(包括空文字)。
- 对 class 和结构类型的 const 成员的引用。
- 对枚举类型成员的引用。
- 带括号的子表达式,它们本身就是常量表达式。
- 转换表达式,前提是目标类型是上面列出的类型之一。
- 预定义的
+
、–
、!
和 ~
一元运算符。
- 预定义
+
、–
、*
、/
、%
、<<
、>>
、&
, |
, ^
, &&
, ||
, ==
, !=
, <
, >
、<=
和 >=
二元运算符,前提是每个操作数都属于上面列出的类型。
?:
条件运算符。
刚刚用 IlSpy
和代码进行了测试:
private static void Main(string[] args)
{
int value = 365 * 24 * 60 * 60;
Console.WriteLine(value);
}
编译后的 MSIL 代码为:
.....
IL_0000: nop
IL_0001: ldc.i4 31536000 // its calculated already
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: call void [mscorlib]System.Console::WriteLine(int32)
IL_000d: nop
IL_000e: ret
} // end of method Program::Main
因此它确实会在编译时计算静态表达式以提高性能,但是如果我们将代码更改为:
double nbr = Math.Sqrt(365 * 24 * 60 * 60);
Console.WriteLine(nbr);
这次最终结果将在 运行 时填充,因为编译器不知道 Math.Sqrt()
在编译时返回的值。
MsIL 代码:
IL_0000: nop
IL_0001: ldc.r8 31536000
IL_000a: call float64 [mscorlib]System.Math::Sqrt(float64)
IL_000f: stloc.0
IL_0010: ldloc.0
IL_0011: call void [mscorlib]System.Console::WriteLine(float64)
IL_0016: nop
IL_0017: ret
} // end of method Program::Main
根据 C# 规范,int test = 1 + 2
中的表达式 1 + 2
被认为是“常量表达式”:
7.19 Constant expressions
A constant-expression is an expression that can be fully evaluated at compile-time.
换句话说:当一个表达式可以在编译时被完全计算时,它被认为是一个“常量表达式”,那些将 在编译时计算。
当您想发现常量表达式和编译时计算的含义时,有点像第 22 条军规。
要将规范的相关部分应用于您的示例:
A constant expression must be [...] a value with one of the following types: [...] int [...].
Only the following constructs are permitted in constant expressions:
Literals (including the null literal).
[...]
The predefined +, –, *, /, %, <<, >>, &, |, ^, &&, ||, ==, !=, <, >, <=, and >= binary operators, provided each operand is of a type listed above.
[...]
Whenever an expression fulfills the requirements listed above, the expression is evaluated at compile-time.
最后一行如果改为:
会更清楚
Whenever an expression fulfills the requirements listed above, the expression is [considered to be a constant expression and will be] evaluated at compile-time.
当表达式与任何列出的规则相矛盾时(包括使用非常量成员调用或方法调用等),它不会被视为常量表达式,因此会在运行时求值。
非常简短的问题,但我现在无法在网上找到解决方案。
int test = 1 + 2;
1 + 2
会在 运行 或编译时执行吗?
提问原因:
我认为大多数人有时会在不说明使用原因或含义的情况下使用文字,因为他们不想通过计算 运行 浪费一点性能,而且我相信计算是在编译期间发生的,没有任何效果关于性能:
int nbr = 31536000; //What the heck is that?
而不是
int nbr = 365 * 24 * 60 * 60; //I guess you know what nbr is supposed to be now...
由于您的示例本质上是常量表达式(即它们仅由常量或计算结果的结构组成),它们将 be evaluated at compile time.
A constant-expression is an expression that can be fully evaluated at compile-time.
The type of a constantexpression can be one of the following: sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, bool, string, any enumeration type, or the null type.
常量表达式中允许以下结构:
- 文字(包括空文字)。
- 对 class 和结构类型的 const 成员的引用。
- 对枚举类型成员的引用。
- 带括号的子表达式,它们本身就是常量表达式。
- 转换表达式,前提是目标类型是上面列出的类型之一。
- 预定义的
+
、–
、!
和~
一元运算符。 - 预定义
+
、–
、*
、/
、%
、<<
、>>
、&
,|
,^
,&&
,||
,==
,!=
,<
,>
、<=
和>=
二元运算符,前提是每个操作数都属于上面列出的类型。 ?:
条件运算符。
刚刚用 IlSpy
和代码进行了测试:
private static void Main(string[] args)
{
int value = 365 * 24 * 60 * 60;
Console.WriteLine(value);
}
编译后的 MSIL 代码为:
.....
IL_0000: nop
IL_0001: ldc.i4 31536000 // its calculated already
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: call void [mscorlib]System.Console::WriteLine(int32)
IL_000d: nop
IL_000e: ret
} // end of method Program::Main
因此它确实会在编译时计算静态表达式以提高性能,但是如果我们将代码更改为:
double nbr = Math.Sqrt(365 * 24 * 60 * 60);
Console.WriteLine(nbr);
这次最终结果将在 运行 时填充,因为编译器不知道 Math.Sqrt()
在编译时返回的值。
MsIL 代码:
IL_0000: nop
IL_0001: ldc.r8 31536000
IL_000a: call float64 [mscorlib]System.Math::Sqrt(float64)
IL_000f: stloc.0
IL_0010: ldloc.0
IL_0011: call void [mscorlib]System.Console::WriteLine(float64)
IL_0016: nop
IL_0017: ret
} // end of method Program::Main
根据 C# 规范,int test = 1 + 2
中的表达式 1 + 2
被认为是“常量表达式”:
7.19 Constant expressions
A constant-expression is an expression that can be fully evaluated at compile-time.
换句话说:当一个表达式可以在编译时被完全计算时,它被认为是一个“常量表达式”,那些将 在编译时计算。
当您想发现常量表达式和编译时计算的含义时,有点像第 22 条军规。
要将规范的相关部分应用于您的示例:
A constant expression must be [...] a value with one of the following types: [...] int [...].
Only the following constructs are permitted in constant expressions:
Literals (including the null literal).
[...]
The predefined +, –, *, /, %, <<, >>, &, |, ^, &&, ||, ==, !=, <, >, <=, and >= binary operators, provided each operand is of a type listed above.
[...]
Whenever an expression fulfills the requirements listed above, the expression is evaluated at compile-time.
最后一行如果改为:
会更清楚Whenever an expression fulfills the requirements listed above, the expression is [considered to be a constant expression and will be] evaluated at compile-time.
当表达式与任何列出的规则相矛盾时(包括使用非常量成员调用或方法调用等),它不会被视为常量表达式,因此会在运行时求值。