我正在尝试收集C#中发生装箱的所有情况:
将值类型转换为System.Object
类型:
struct S { }
object box = new S();
Run Code Online (Sandbox Code Playgroud)将值类型转换为System.ValueType
类型:
struct S { }
System.ValueType box = new S();
Run Code Online (Sandbox Code Playgroud)将枚举类型的值转换为System.Enum
类型:
enum E { A }
System.Enum box = E.A;
Run Code Online (Sandbox Code Playgroud)将值类型转换为接口引用:
interface I { }
struct S : I { }
I box = new S();
Run Code Online (Sandbox Code Playgroud)在C#字符串连接中使用值类型:
char c = F();
string s1 = "char value will box" + c;
Run Code Online (Sandbox Code Playgroud)
注意:char
类型的常量在编译时连接在一起
注意:由于6.0版的C#编译器优化串联涉及bool
,char
,IntPtr
, …
这更像是一个'奇怪的原因',而不是一个特定的问题,但请看下面的代码
static void Main(string[] args)
{
int val = 10;
Console.WriteLine("val is {0}", val); // (1)
Console.WriteLine("val is {0}", val.ToString()); //(2)
}
Run Code Online (Sandbox Code Playgroud)
在情况(1)中输出以下IL
IL_0000: nop
IL_0001: ldc.i4.s 10
IL_0003: stloc.0
IL_0004: ldstr "val is {0}"
IL_0009: ldloc.0
IL_000a: box [mscorlib]System.Int32
IL_000f: call void [mscorlib]System.Console::WriteLine(string,
object)
Run Code Online (Sandbox Code Playgroud)
在我明确调用toString方法的情况下(2)我得到了
IL_0014: nop
IL_0015: ldstr "val is {0}"
IL_001a: ldloca.s val
IL_001c: call instance string [mscorlib]System.Int32::ToString()
IL_0021: call void [mscorlib]System.Console::WriteLine(string,
object)
Run Code Online (Sandbox Code Playgroud)
所以在case(1)中,即使int重写toString,也会将值类型装箱并调用toString方法,这可能会调用vtable覆盖
所以结果完全相同,但显式的toString避免了装箱操作
谁知道为什么?
=编辑=
确定要清楚,令我感到困惑的是,我开始假设即使int派生自System.ValueType,而System.ValueType又派生自System.Object,因为它包含toString,GetHashCode等.
所以在我的天真view(可能来自C++),如果我重写从System.Object派生的方法,那么就不需要强制转换为System.Object(因此将值类型框),因为存在overriden方法,编译器将自动引用vtable条目对于类型.
我也假设调用Console.WriteLine()隐式调用int.toString所以也许这就是我出错的地方.希望有道理
好的 - 全部排序.谢谢大家直截了当.所有这些都与我的糟糕假设有关,即Console.WriteLine正在进行隐式字符串转换.不要问我为什么这么想 - 看起来很明显现在错误:)