这是一个'奇怪的原因'而不是一个特定的问题,但请看下面的代码
static void Main(string[] args)
{
int val = 10;
Console.WriteLine("val is {0}", val); // (1)
Console.WriteLine("val is {0}", val.ToString()); //(2)
}
在情况(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)
在我明确调用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)
所以在case(1)中,即使int重写toString,也会将值类型装箱并调用toString方法,然后可能会调用vtable override
因此结果完全相同,但显式的toString避免了装箱操作
任何人都知道为什么?
=编辑=
可以清楚,令我困惑的是,我开始假设即使int派生自System.ValueType,而System.ValueType又派生自System.Object,因为它包含toString,GetHashCode等。
所以在我的天真视图中(可能来自C ++),如果我覆盖从System.Object派生的方法,那么就不需要强制转换为System.Object(并因此将值类型框),因为存在overriden方法并且编译器将自动引用该类型的vtable条目
我也假设调用Console.WriteLine()隐式调用int.toString所以也许这就是我出错的地方。希望有意义
好的 - 全部排序。谢谢大家直截了当。所有这些都与我的糟糕假设有关,即Console.WriteLine正在进行隐式字符串转换。不要问我为什么这么想 - 现在看来有多么明显错误:)
答案 0 :(得分:13)
您根本没有暗中调用ToString
。 <{1}}方法没有重载,它接受格式字符串后面的字符串,只接受对象。
因此,您并未暗中调用WriteLine
,而是隐式将ToString
转换为int
。第一种情况相当于:
object
由于Console.WriteLine("val is {0}", (object)val);
是值类型,因此发生装箱。
第二种情况相当于:
int
由于字符串是引用类型,将其强制转换为对象实际上不会导致发出任何代码。它只是将类型与方法签名匹配。
答案 1 :(得分:4)
因为在第一个实例中,您在调用int
函数时将object
作为Console.WriteLine()
传递。这会强制int
装箱。在第二种方法中,您直接调用ToString
,这可以避免装箱,并将string
传递给WriteLine
,这已经是参考类型。
答案 2 :(得分:2)
在第一次调用中根本没有.ToString调用。而是调用函数Console.WriteLine(object)。第一个参数是int类型,必须加框以满足类型对象。稍后在WriteLite内部,将在对象上调用.ToString。