一道简单的面试题引发的思考
前几天微信好友突然发给我一个截图打开一看是一段代码,题目如下,问装箱和拆箱的次数以及最终的输出。
namespace DotNetCore
{
class Program
{
static void Main(string[] args)
{
var p = new Point(1,1);
Console.WriteLine(p);
p.Change(2,2);
Console.WriteLine(p);
object o = p;
((Point)o).Change(3,3);
Console.WriteLine(o);
}
}
struct Point
{
private int a,b;
public Point(int a,int b)
{
this.a = a;
this.b = b;
}
public void Change(int a,int b)
{
this.a = a;
this.b = b;
}
public override string ToString()
{
return $"({a},{b})";
}
}
}
看了看很简单啊,不就是考值类型和引用类型吗?so easy啊,甩手一个答案:
装箱1次,拆箱1次,
返回结果:
(1,1)
(2,2)
(2,2)
事实证明这个答案只能算答对了一半,题目套路深啊!!!
图说执行流程
最直观,最简单,最有效的当然是直接上IL了,IL是啥?IL就是代码编译后的中间语言(Intermediate Language)。上面Main方法代码的IL详见下图。
相信上图已经很详细的说明的代码的一个执行流程,即使对IL并不熟悉的人应该也是可以看懂整个代码的执行的流程的。这个题目的坑点我认为主要有两个:
- ((Point)o).Change(3,3); 可能有部分人会认为输出是(3,3),通过上图IL_0044和IL_0045两行,我们可以很清晰的知道 ((Point)o)拆箱后的变量我们后续并没有使用。输出的还是o变量本身的值;
- Console.WriteLine(); 语句,该方法并没有struct类型的重载,所以调用的是使用Object类型参数的方法,所以每次输出都要进行装箱操作,通过IL我们也可以很清晰的看到。
所以,本题正确答案如下:
装箱3次,拆箱1次,
返回结果:
(1,1)
(2,2)
(2,2)
一些思考
值类型的装箱和拆箱是会带来性能损耗的,因为数据需要不断的在线程栈(stack)和托管堆(manager Heap)上来回移动,有时候我们不经意使用的一些方法或者类型,就包含了隐式装箱,例如:
Hashtable ht = new Hashtable();
ht.Add(a,b);
当a和b分别是值类型或引用类型,执行效率是不一样的,如果是大批量数据处理问题可想而知。
技术来不得半点马虎。So,我觉得无论是自己写程序,还是使用第三方的一些API,都应该更深入了解一些内部的实现,拿来即用虽然省事,但不知哪一天可能就入坑了。如有问题欢迎讨论。
文中如有不对之处,欢迎指正。 | ||
本文版权归作者和博客园共有,欢迎转载,但请在文章页面明显位置给出原文连接。 | ||
作者:Nuss | ||
出处:http://www.cnblogs.com/Nuss/ |