为什么int的插值表达式会出现装箱?
Why does boxing occur in the interpolation expression of int?
例如下面的代码:
int n=1;
string str=$"{n}";
但是显式添加ToString()
后,装箱就不会发生了。
int n=1;
//The compiler will recommend removing the explicit call of the ToString() method
string str=$"{n.ToString()}";
书CLR via C#
写到String.Format
会在内部调用ToString
方法来获取对象的字符串表示。
既然内部调用了ToString方法,为什么会出现示例1中的装箱?
“调用ToString
”并不是防止拳击的妙法。如果您在 ToString
它被装箱后调用 ToString
,装箱仍然会发生,字符串插值就是这种情况。
如您所知,字符串插值通常会对 string.Format
调用脱糖。如果您查看 list of overloads available,您会发现没有采用像 int
或 long
这样的值类型的重载。每个重载都需要 object
。要将 int
传递给这些方法,首先需要对其进行装箱。 string.Format
然后在某个时候 在盒装 object
.
上调用 ToString
将此与在字符串插值中直接调用 ToString
进行比较。没有到引用类型的转换 (object
),所以没有装箱。
我注意到,尽管您的代码使用的是 C# 内插字符串,但它并未使用 FormattedString
class,因为 C# 编译器只会在以下情况下使用 FormattedString
内插字符串被直接分配给 FormattedString
类型的变量、字段或参数(我不同意,但无论如何)。
The book CLR via C# writes that String.Format will call the ToString method internally to get the string representation of the object.
是的,但是所有 String.Format
重载都使用 Object
类型的参数或 params Object[]
,这必然意味着装箱其参数。
why does the boxing occur in Example 1?
因为它必须将int n
传递给Object arg0
。
这是我在 LinqPad(C# 8.0,启用了编译器优化)中编译您的第一个代码块时生成的 IL:
IL_0000: ldc.i4.1
IL_0001: stloc.0
IL_0002: ldstr "{0}"
IL_0007: ldloc.0
IL_0008: box System.Int32
IL_000D: call System.String.Format
IL_0012: pop
IL_0013: ret
您可以在指令偏移 IL_0008
处看到 box
指令,就在它传递到 String.Format
之前。
为了格式化字符串,它只能采用 Object 类型,它调用 ToString() 以将其连接起来。装箱基本上是将值类型转换为引用类型,这是在隐式转换为对象时发生的事情,因此它可以存储在堆中。
例如下面的代码:
int n=1;
string str=$"{n}";
但是显式添加ToString()
后,装箱就不会发生了。
int n=1;
//The compiler will recommend removing the explicit call of the ToString() method
string str=$"{n.ToString()}";
书CLR via C#
写到String.Format
会在内部调用ToString
方法来获取对象的字符串表示。
既然内部调用了ToString方法,为什么会出现示例1中的装箱?
“调用ToString
”并不是防止拳击的妙法。如果您在 ToString
它被装箱后调用 ToString
,装箱仍然会发生,字符串插值就是这种情况。
如您所知,字符串插值通常会对 string.Format
调用脱糖。如果您查看 list of overloads available,您会发现没有采用像 int
或 long
这样的值类型的重载。每个重载都需要 object
。要将 int
传递给这些方法,首先需要对其进行装箱。 string.Format
然后在某个时候 在盒装 object
.
ToString
将此与在字符串插值中直接调用 ToString
进行比较。没有到引用类型的转换 (object
),所以没有装箱。
我注意到,尽管您的代码使用的是 C# 内插字符串,但它并未使用 FormattedString
class,因为 C# 编译器只会在以下情况下使用 FormattedString
内插字符串被直接分配给 FormattedString
类型的变量、字段或参数(我不同意,但无论如何)。
The book CLR via C# writes that String.Format will call the ToString method internally to get the string representation of the object.
是的,但是所有 String.Format
重载都使用 Object
类型的参数或 params Object[]
,这必然意味着装箱其参数。
why does the boxing occur in Example 1?
因为它必须将int n
传递给Object arg0
。
这是我在 LinqPad(C# 8.0,启用了编译器优化)中编译您的第一个代码块时生成的 IL:
IL_0000: ldc.i4.1
IL_0001: stloc.0
IL_0002: ldstr "{0}"
IL_0007: ldloc.0
IL_0008: box System.Int32
IL_000D: call System.String.Format
IL_0012: pop
IL_0013: ret
您可以在指令偏移 IL_0008
处看到 box
指令,就在它传递到 String.Format
之前。
为了格式化字符串,它只能采用 Object 类型,它调用 ToString() 以将其连接起来。装箱基本上是将值类型转换为引用类型,这是在隐式转换为对象时发生的事情,因此它可以存储在堆中。