2009-09-21 48 views
4

我對這段代碼有點困惑。c# - ref修飾符...參考類型

public static void Foo(A p) 
    { 
     p.SomeProp = "ccc"; 
     p = null; // !!! 
    } 

    static void Main(string[] args) 
    { 
     A p = new A(); 
     Foo(p); 

     Console.WriteLine("SomeProp is: " + p.SomeProp); 
     Console.ReadLine(); 
    } 

輸出是:

「SomeProp是:CCC

但我本來期望一個NullReferenceException。


但是,如果我改變它,像這樣,使用ref修飾符:

public static void Foo(ref A p) 
    { 
     p.SomeProp = "ccc"; 
     p = null; 
    } 

    static void Main(string[] args) 
    { 
     A p = new A(); 
     Foo(ref p); 

     Console.WriteLine("SomeProp is: " + p.SomeProp); 
     Console.ReadLine(); 
    } 

我得到一個NullReferenceException - 第二個是可以理解我。

但是怎麼會這樣呢,在第一段代碼中,p沒有設置爲空,但是該屬性獲取了它的值集?

我的問題是:如果第一段代碼中的Foo方法不是對原始實例的引用,那麼它的p參數是什麼?


Btw。這裏是A類的定義

public class A 
{ 
    public string SomeProp; 
} 
+0

另請參閱:http://stackoverflow.com/questions/186891/why-use-ref-keyword-when-passing-an-object – 2009-09-21 15:56:49

回答

5
p.SomeValue = "ccc"; 

是說:

  • 獲取使得p對象就是
  • 基準組對象的屬性someValue中的值設定爲「CCC」

    p = null;

是說:

  • 變化p到,而不是指它使用的對象,現在指空。

這並不是說改變p指向null的對象,而是局部變量p現在應該引用null。

默認情況下,當您在方法調用「Foo(p)」中傳遞類型A的參數時,不會傳遞由p引用的對象,或者引用p,但引用由p引用的對象。它們引用同一個對象,但它們不是同一個參考。即「public static void Foo(A p)」中的引用p與「Foo(p)」中的p不是相同的引用,但它們引用相同的對象。

您可以通過改用ref參數來改變這種行爲。這改變它使得它們是相同的參考,並且改變其值的值改變另一個的值。

+0

希望我現在得到它,感謝你和所有其他人的解釋 – Max 2009-09-22 07:37:12

1

在第一個函數中,您只是將p的本地引用設置爲null。你沒有將主要設置爲null。在第二個函數中,由於使用了ref,所以你的將p設置爲null。

17

在.Net中,一切通過值傳遞,除非您明確使用refout關鍵字。對於參考類型,這意味着傳遞參考的副本。

在你的第一個例子中,這意味着你的p變量仍然是對同一個對象的引用,因此設置一個屬性就像你期望的那樣工作。但是,當您將引用設置爲null時,所有更改都是副本。

+0

+1因爲短而準確的答案 – Max 2009-09-22 07:36:37

1

除非您使用out/ref,否則C#使用值傳遞。當你通過價值傳遞參考。該參考被複制。但是,由於它仍然引用堆中的同一對象,因此可以通過引用修改對象的狀態。如果您使用ref您傳遞參考地址。如果在此之後訪問原始引用,則取消該引用將會刪除實際引用,因此會刪除NullReferenceException

3

p參數是對您創建的新Foo實例的引用的副本。把它想象成一個路標:調用「new A()」在堆上創建一個A對象,並且給你一個路標給它,你存儲在p中。然後你調用Foo函數,給它一個路標的副本 - 它知道如何去A對象,並更新屬性。然後它在整個路標上塗鴉 - 它不能再到那裏了,但是物體依然存在。調用者仍然有一個有效的路標,所以不會引發異常。

第二個例子,用「ref」參數有效地說「不要給我一個路標的副本,給我實際的路標」。這一次,當它遍佈它時,傳遞給該函數的原始信息也會丟失,併發生異常。

在更多的技術術語,C#中,沒有裁判的關鍵字,總是「按值調用」 - 所有參數都傳入的參數值的副本。爭論的價值是一個路標(或「參考」)的事實並不重要。

+0

+1爲好的路標例如,它幫助了我很多 – Max 2009-09-22 07:35:52

4

對象引用按值傳遞。 < - 非常重要。

你的方法有複製引用,這樣的,當你說p = null,只更改該引用的副本。當您返回時,原始參考「p」仍具有原始值。

在第二個例子中,通過參考明確地傳遞你的對象。所以,新的引用(null)被傳回給你的調用函數(然後給你null異常)。

-1

當你路過valule你可以改變其狀態的對象引用。但是當你通過引用傳遞一個對象引用時,你不僅可以改變它的狀態,還可以改變實際的對象本身

編輯:後知道對象永遠不會爲值或引用傳遞,而是一個對象的引用作爲值或引用被傳遞。

0

如何在調試時將「p」添加到監視列表併爲其生成對象ID,該ID與函數Foo(p)中的「p」的ID相同。所以基本上調用方法中的「p」和Foo中的「p」都具有相同的對象ID。