object.ToString() 如何处理盒装值类型?
How does object.ToString() work on a boxed value type?
class Program {
static void Main(string[] args) {
Int32 i = 123;
Double d = 123.456;
FunPrint(i);
FunPrint(d);
}
static void FunPrint(object obj) {
Console.WriteLine(obj);
}
}
我对这个示例的理解是,FunPrint()
首先创建一个新对象,并根据传递的 ValueType 的值构建它(在本例中为 Int32
)。其次,调用 Object.ToString()
并正确显示特定于 ValueType 的字符串格式。
值类型不包含虚函数所以...
我不明白的是 Object
如何知道它在内部持有什么类型以便进行正确的字符串格式化。
调用函数在调用 FunPrint
之前将参数装箱。
Value types do not contain virtual functions so...
实际上他们可以。您可以从值类型实现接口。你不能从一个派生,这限制了覆盖的级别。
然而,要虚拟调用虚函数函数,您需要将值类型装箱。
该机制同样适用于此。该值已装箱,因此您可以调用其虚拟成员。
编辑以阐明在值类型上调用接口方法:
var i = 123;
i.ToString(); // not boxed, statically resolves to Int32.ToString()
var o = (object)o; // box
o.ToString(); // virtual call to object.ToString()
var e = (IEquatable<int>)i; // box
i.Equals(123); // virtual call to IEquatable<int>.Equals(int)
编辑以包含 Jon Hanna 的建议。在值类型上调用 非-虚方法 System.Object
确实 需要装箱。
var i = 1234;
i.GetType(); // boxes!
在对应的IL中可以看到:
ldc.i4.s 123
stloc.0 // i
ldloc.0 // i
box [mscorlib]System.Int32
call instance class [mscorlib]System.Type [mscorlib]System.Object::GetType()
Object.GetType()
无法虚拟调用,签名为:
public extern Type GetType();
还是需要装箱
Value types do not contain virtual functions so...
真的吗?
struct Foo
{
public override string ToString() =>
"Sure looks like a virtual call";
public override bool Equals(object obj) =>
"So does this one";
}
所有值类型都继承自 object
,您可以完美覆盖任何虚拟方法。您无法进一步扩展它们这一事实无关紧要。
对于其他同样bash他们反对box/unbox魔法的人,我在这里找到了一些关于这个主题的可靠的进一步阅读:
http://mattwarren.org/2017/08/02/A-look-at-the-internals-of-boxing-in-the-CLR/
class Program {
static void Main(string[] args) {
Int32 i = 123;
Double d = 123.456;
FunPrint(i);
FunPrint(d);
}
static void FunPrint(object obj) {
Console.WriteLine(obj);
}
}
我对这个示例的理解是,FunPrint()
首先创建一个新对象,并根据传递的 ValueType 的值构建它(在本例中为 Int32
)。其次,调用 Object.ToString()
并正确显示特定于 ValueType 的字符串格式。
值类型不包含虚函数所以...
我不明白的是 Object
如何知道它在内部持有什么类型以便进行正确的字符串格式化。
调用函数在调用 FunPrint
之前将参数装箱。
Value types do not contain virtual functions so...
实际上他们可以。您可以从值类型实现接口。你不能从一个派生,这限制了覆盖的级别。
然而,要虚拟调用虚函数函数,您需要将值类型装箱。
该机制同样适用于此。该值已装箱,因此您可以调用其虚拟成员。
编辑以阐明在值类型上调用接口方法:
var i = 123;
i.ToString(); // not boxed, statically resolves to Int32.ToString()
var o = (object)o; // box
o.ToString(); // virtual call to object.ToString()
var e = (IEquatable<int>)i; // box
i.Equals(123); // virtual call to IEquatable<int>.Equals(int)
编辑以包含 Jon Hanna 的建议。在值类型上调用 非-虚方法 System.Object
确实 需要装箱。
var i = 1234;
i.GetType(); // boxes!
在对应的IL中可以看到:
ldc.i4.s 123
stloc.0 // i
ldloc.0 // i
box [mscorlib]System.Int32
call instance class [mscorlib]System.Type [mscorlib]System.Object::GetType()
Object.GetType()
无法虚拟调用,签名为:
public extern Type GetType();
还是需要装箱
Value types do not contain virtual functions so...
真的吗?
struct Foo
{
public override string ToString() =>
"Sure looks like a virtual call";
public override bool Equals(object obj) =>
"So does this one";
}
所有值类型都继承自 object
,您可以完美覆盖任何虚拟方法。您无法进一步扩展它们这一事实无关紧要。
对于其他同样bash他们反对box/unbox魔法的人,我在这里找到了一些关于这个主题的可靠的进一步阅读:
http://mattwarren.org/2017/08/02/A-look-at-the-internals-of-boxing-in-the-CLR/