2014-03-12 77 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)。