2014-03-12 78 views
4

它只是一个理论的问题,但我不能得到一个很好的答案:如果我传递一个参数由ref,本身传递的对象,而不是一个副本参考 - 参数 - 堆栈或堆

这是让我困惑的事情:据我所知,每种方法都有自己的堆栈帧 - 内存,他们不能离开。那么这是否意味着一个ref - Object被堆积在Heap上,并且存在对该参数的引用,或者该方法是否进入调用方法的堆栈并在那里工作?

对不起,如果我的问题很混乱,我基本上想知道ref类型是如何保存的以及它有什么影响。

伊迪丝: 我想我没有让自己清楚。我理解价值和适应类型的概念。为了方便起见,我尝试仅通过值类型来解释它,可以说Int:

步骤1通过传递一个Int ByVal来调用Prodecure 2。这个int在Prodecure 2栈中有它自己的内存,这意味着,在P2中修改这个值并不会改变P1中的值,因为这2个值被保存在每个栈中一次。

现在与byref相同:Prodecure 2不保存Int的副本,但可以直接访问该值。有(在我Oppinion)两大possibilies,使这项工作:

  1. 的int被包装上堆,并且有实际上2指点 这种诠释,但由于其目前在堆上,值两个Prodecures上的变化都可以看到 。
  2. P2已经可以访问P1堆栈了,我认为这是不可能的,因为这意味着, Stack没有在Stone中设置。

这是否使得它更清楚我的意思?

+1

这可能对你有帮助:http://www.yoda.arachsys.com/csharp/parameters.html –

+0

每种方法都有自己的堆栈? –

+0

@RoyiNamir嗯,至少是一个堆栈框架,而不是它自己的堆栈。 –

回答

8

传递的参数是某个对象的地址。该引用在堆栈上传递,以及该方法的所有其他参数。

在你调用方法之前,实际的对象本身存在于它所居住的任何地方。这可能在堆栈中,它可能在堆中,没关系。通过引用传递对象的行为不会导致它在内存中移动,从堆栈到堆或从堆到堆栈。

+1

我的印象是,对象*可以*在内存中移动(除非固定),并且引用将以某种方式遵循(直接更新或双重间接)。否则,一旦存在对象的引用,GC就很难完成其工作。 –

+2

@FrédéricHamidi这是一个有点不同的抽象层。 (从引用堆中某些东西的任何对象的角度来看,它根本不会移动,因为它们的所有引用都总是在同一个地方.CG命中一个不同的内存块来解析地址在任何情况下都不会移动,因为该对象是通过引用传递的,这是一个移动的对象,因为GC正在执行一个集合。我的主要观点是,不像说一个闭包,这个变量在'ref'传递时不会被挂起。 – Servy

+0

我相信,除非我们明确地将它与“固定”声明挂钩。但通过“固定”,我们确保GC在工作时,物体应保持在原位(不可移动)。否则,只需通过参考;是的,物体不会移动到任何地方。 – Rahul

1

尽管Servy已经正确回答了这个问题,但看起来有很多混淆关于传递一个参数与ref以及通过值传递对象的引用之间的区别。出于这个原因,我认为值得提供一个简短的例子。

假定以下简单的类:

class Player 
{ 
    public Player(int health) 
    { 
     Health = health; 
    } 
    public int Health { get; set; } 
} 

现在我们可以测试更新对象的属性,并且还改变参考本身:

static void Main(string[] args) 
{ 
    Player player = new Player(100); 
    Console.WriteLine(player.Health); 

    ChangeHealth(player); 
    Console.WriteLine(player.Health); 
    ChangeHealthByRef(ref player); 
    Console.WriteLine(player.Health); 

    ChangePlayer(player); 
    Console.WriteLine(player.Health); 
    ChangePlayerByRef(ref player); 
    Console.WriteLine(player.Health); 
} 

static void ChangeHealth(Player player) 
{ 
    player.Health = 80; 
} 

static void ChangeHealthByRef(ref Player player) 
{ 
    player.Health = 60; 
} 

static void ChangePlayer(Player player) 
{ 
    player = new Player(40); 
} 

static void ChangePlayerByRef(ref Player player) 
{ 
    player = new Player(20); 
} 

输出:

100 
80 
60 
60 
20 

ChangeHealth成功修改了Health属性player对象。 ChangeHealthByRef也成功修改了player对象的Health属性。因此,您可以在两次调用中看到player所指的对象可以被修改,尽管ChangeHealth使用了该引用的副本。

现在,这里的一部分,我认为人们感到困惑:

ChangePlayer创建一个新的Player对象,修改了通过引用的副本。这意味着变更不会在调用代码中反映(即Health仍= 60)。 ChangePlayerByRef还创建一个新对象Player但是,此时,它的修改参考直接,这意味着变化反映在调用代码(即Health = 20)。