是否在编译时对 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.

当表达式与任何列出的规则相矛盾时(包括使用非常量成员调用或方法调用等),它不会被视为常量表达式,因此会在运行时求值。