一道面试题:解析方法中对象存放以及堆栈之间的关系

朋友面试遇到一道题是这样的:

 

 

namespace Demo
{
    
    
internal class Program
    {
        
private static void Main()
        {
            Person p
=new Person("alex");
            Upate(p);
            Console.WriteLine(p.ToString());
            Console.Read();
        }
        
static void Upate(Person p)
        {
            p
=new Person("franck");
        }
    }

    
internal class Person
    {
        
private string _name;
        
public Person(string name)
        {
            
this._name=name;
        }

        
public override string ToString()
        {
            
return this._name;
        }
    }
}

1,输出的结果是?

2,分析其执行过程

 

 

答案是输出结果是:alex

为什么会这样呢,这才是问题所在。好多人会认为在、执行Update方法时对象p发生了改变,事实也是这样的,但为什么结果没有变化,要分析清楚原因,还得看IL

entrypoint
  // Code size       
38 (0x26)
  .maxstack  
2
  .locals init ([
0] class Demo.Person p)
  
IL_0000:  nop
  
IL_0001:  ldstr      "alex"
  
IL_0006:  newobj     instance void Demo.Person::.ctor(string)
  
IL_000b:  stloc.0    //从计算堆栈的顶部弹出当前值并将其存储到索引 0 处的局部变量列表中
  
IL_000c:  ldloc.0    //将索引 0 处的局部变量加载到计算堆栈上
  
IL_000d:  call       void Demo.Program::Upate(class Demo.Person)
  
IL_0012:  nop
  
IL_0013:  ldloc.0   //将索引 0 处的局部变量加载到计算堆栈上
  
IL_0014:  callvirt   instance string [mscorlib]System.Object::ToString()
  
IL_0019:  call       void [mscorlib]System.Console::WriteLine(string)
  
IL_001e:  nop
  
IL_001f:  call       int32 [mscorlib]System.Console::Read()

Upate Method
.method private hidebysig static void  Upate(class Demo.Person p) cil managed
{
  // Code size       
14 (0xe)
  .maxstack  
8
  
IL_0000:  nop
  
IL_0001:  ldstr      "franck"
  
IL_0006:  newobj     instance void Demo.Person::.ctor(string)
  
IL_000b:  starg.s    p  //将位于计算堆栈顶部的值存储在参数槽中的指定索引处
  
IL_000d:  ret         //从当前方法返回,并将返回值(如果存在)从调用方的计算堆栈推送到被调用方的计算堆栈上。
} // end of method 
Program::Upate

 

这是所有执行的IL吗,根据我做的注释,这一下应清楚了,事实上Update方法执行是在另一个堆栈上,这个堆栈上的执行结果在没有推送到调用方堆栈,在Main()方法的堆栈中,p对象还是原来的对象。

 

图示:

Main()

Person p=new Person("alex");

 

Upate 方法同样会产生一个这样的堆栈,方式和上面相同。关键就在这一句上了

IL_000d: ret        //从当前方法返回,并将返回值(如果存在)从调用方的计算堆栈推送到被调用方的计算堆栈上。

Update堆栈上的对象没有推送到Main()产生的堆栈上的时间,图1堆栈中存放的对象p不变,因而输出结果就是那样!

 

 

posted @ 2011-03-20 01:36  haifengalex  阅读(529)  评论(0)    收藏  举报