2012-03-19 68 views
1

我做了一個函數,我想在其中傳遞字符串數組,像string [] aa = null
在函數定義中我做了一些更新,然後我發現它的值是當函數執行/返回時沒有更新。我無法理解C#中哪些事情需要提及ref關鍵字。它不是總是通過參考傳遞參數爲什麼需要提及ref?哪些是需要與ref一起提及才能通過引用傳遞的對象?在C#函數參數中提到的參考

代碼是這樣的,只是試圖顯示我在做什麼,我無法更新值,而不使用ref。

string[] temp = null 
foo(ref temp); 
//function definition 
void foo (ref string[] temp) 
{ 
temp = {"Hello World ","You must be updated now"} 
} 
foreach(string s in temp) 
System.Console.WriteLine(s) 
+0

這樣的社區,我閱讀所有的答案,並學到很多 – 2012-03-19 17:34:02

+0

那麼,爲什麼你的代碼沒有工作? – 2012-03-19 17:41:44

+0

我的代碼有效,我需要理解這個概念,我是C++開發人員,通過學習它的基本概念來學習新的語言。 – 2012-03-19 17:50:56

回答

1

首先,你的僞代碼應該工作。但是,在我們開始之前,這裏有三件事:值類型,引用類型和「ref」關鍵字。

值類型通常是你簡單的基本類型,如int,double等。字符串是一個奇怪的類型,因爲它被認爲是一個值類型。

更復雜的類型,如數組和類是引用類型。

當您傳遞值類型如int和double時,則傳遞值的副本,所以如果將int x = 10傳遞給方法,則在離開方法後,方法中的x更改將不會反映出來。另一方面,如果您通過MyClass class1,則屬性 class1中的任何更改都將反映在該函數之外。只是不要嘗試在你的方法中新建一個新的class1,因爲這在調用者之外不會改變。

如果您想更改方法中的值類型,請通過ref。如果你想新建一個新的類或數組,那麼你通過ref。

還有一件事:它不是使用之間與參考之間的黑白。只有當方法的設計始終只在方法內創建類或數組時,纔會使用它。如果你想允許創建一個新的對象,你可以在引用類型上使用ref。像,

//function definition 
    void foo (ref string[] temp) 
    { 
     if(temp == null) 
     { 
      temp = new string[] { "Hello World ", "You must be updated now" }; 
     } 
     else 
     { 
      // do something with the existing temp 
     } 
    } 

最後,如果這是你的實際代碼:

 string[] temp = null; 
     foo(ref temp); 

     foreach (string s in temp) 
      System.Console.WriteLine(s); 

後來:

//function definition 
    void foo (ref string[] temp) 
    { 
     temp = new string[] { "Hello World ", "You must be updated now" }; 
    } 

然後,它實際上應該工作。

+0

謝謝,SO社區真棒 – 2012-03-19 17:29:22

+0

值類型通常是你簡單的基本類型,如int ,double等等。字符串是一個奇怪的字符串,因爲它被認爲是一個值類型。 更復雜的類型,如數組和類是引用類型。 – 2012-03-19 17:35:49

+0

非常好的一點 – 2012-03-19 17:36:00

4

你有點誤會錯綜複雜。如果你傳遞一個引用對象,那麼它會通過值傳遞引用(繼續閱讀並希望它應該開始變得更有意義)。但是,這並不意味着它是通過ref。事實上,默認情況下,C#參數是按值傳遞的。

Here is a good article that explains this pretty well

而且,這是解釋我的意思,當我說,你是按值

通過參考在C#中的片段,參數是價值,意義傳遞(默認)那 它們在傳遞給方法時被隱式複製。對於值型 參數,這意味着在物理複製實例(在相同 方式P2被複制),而對於引用類型它意味着複製 參考(以相同的方式F2被複制)

。然而,在你的情況下,你傳遞你的對象,然後在你的方法中創建一個帶有新引用的新對象。因此,原始引用保持不變,您可以通過執行更新而不是全新的創建來看到這一點。當你明確地說ref的時候,你現在傳遞了對引用的引用,所以它可以工作,因爲你只使用指向引用的指針,並且當你創建新對象時,它將被放置到該引用位置。

正如eouw0o83hf提到的,如果你正在創建一個全新的對象,那麼你應該使用out來表示這個。 ref通常更多用於值對象,它們不通過引用傳遞。

總結:

  • 如果是值類型,以及要更新的價值,有它處處體現,那麼你需要使用ref
  • 如果是引用類型
    • 如果您希望更新的值,以便它處處體現,那麼您可以在正常通過它(無refout
    • 如果你想在方法內部創建一個全新的實例,有反映,那麼你應該使用out

UPDATE

Here is an MSDN article explaining exactly what you are asking :)

+0

是的,請檢查,更新它在功能或通過引用它必須要求ref關鍵字,我也希望沒有辦法使用ref前綴通過引用傳遞的場景 – 2012-03-19 16:39:28

+1

我已經在驗證後更新了我的描述。如果你有一個C++或其他語言背景的指針,那麼這將變得很明顯,否則希望我解釋得足夠好:) – 2012-03-19 16:56:03

+0

@JustinPihony,你的第一段有點矛盾,因爲上半段說它不會沒有工作(「原始參考保持不變」,但沒有),但第二個說它確實有效。等等......讓我試試我的答案...... – 2012-03-19 17:06:29

2

C#總是價值傳遞參數,除非你使用refout修飾。

string[] temp = { "zero", "one", "two" }; 
MutateByVal(temp); 
MutateByRef(ref temp);  

void MutateByVal(string[] arr) 
{ 
    // arr and temp are separate references to the same array 
    // the value of arr (a reference) is a copy of the value of temp 

    // mutate the array referenced by arr 
    arr[1] = "mutated!"; 
    // arr and temp still point at the same array 
    // so both arr and temp now contain { "zero", "mutated!", "two" } 

    // re-assign arr 
    arr = new[] { "blah", "blah", "blah" }; 
    // arr and temp now point at different arrays 
    // arr now contains { "blah", "blah", "blah" } 
    // temp still contains { "zero", "mutated!", "two" } 
} 

void MutateByRef(ref string[] arr) 
{ 
    // arr is an alias for temp 
    // that is, they are two different names for the same reference 

    // mutate the array referenced by arr 
    arr[1] = "mutated!"; 
    // arr and temp are the same reference 
    // so both arr and temp now contain { "zero", "mutated!", "two" } 

    // re-assign arr 
    arr = new[] { "blah", "blah", "blah" }; 
    // arr and temp are the same reference 
    // so both arr and temp now contain { "blah", "blah", "blah" } 
} 
+0

「C#總是按值傳遞參數」 - 我認爲這個語句有點亂碼,因爲引用類型的對象是通過引用傳遞的。 'MutateByRef'也可以不用'ref'修飾符。 – eouw0o83hf 2012-03-19 17:30:15

+0

謝謝,SO社區真棒 – 2012-03-19 17:31:08

+0

@ eouw0o83hf:我不同意。什麼是亂碼*「C#總是按值傳遞參數,除非使用ref或out修飾符」*?引用類型的參數是按值傳遞的,只不過這些值恰好是這些情況下的引用(參見上面的'MutateByVal')。 – LukeH 2012-03-19 18:06:58

2

這是因爲你實際上是返回一個全新的對象而不是修改現有條目。如果您正在分配新對象,則應該使用out

下面是一個示例,顯示out,ref和常規傳遞如何在數組arg上工作。正如你所看到的那樣,無論指定了ref,數組都是通過引用傳遞的;但是,如果返回一個全新的對象,你需要指定out

class Program 
{ 
    static void Main(string[] args) 
    { 
     string[] val; 
     foo(out val); 
     Console.WriteLine(string.Join(",", val)); 
     // Output: 1, 2 

     bar(ref val); 
     Console.WriteLine(string.Join(",", val)); 
     // Output: modified, 2 

     bar2(val); 
     Console.WriteLine(string.Join(",", val)); 
     // Output: modified again, 2 

     Console.Read(); 
    } 

    static void foo(out string[] temp) 
    { 
     temp = new string[]{"1", "2"}; 
    } 

    static void bar(ref string[] temp) 
    { 
     temp[0] = "modified"; 
    } 

    static void bar2(string[] temp) 
    { 
     temp[0] = "modified again"; 
    } 
} 
+0

這是C#使用傳遞引用的方式嗎?是否有可能做到這一點沒有任何參考或關鍵字? – 2012-03-19 16:43:44

+0

是的,這是參考傳遞的工作原理。如果您想修改現有數組的成員,則不需要指定'ref'。如果你返回一個全新的對象,你需要指定'out'。 – eouw0o83hf 2012-03-19 16:48:00

+0

謝謝,SO社區很棒 – 2012-03-19 17:31:32

1

沒有ref參考陣列被簡單地複製並傳遞給方法(按價值 - 因爲引用類型的是對象的引用);允許該方法訪問相同的數組,但它確實允許它修改調用者自己的引用。

這就是ref(或確實是out)關鍵字的用途。

但是 - 你並不需要使用ref,如果你只是想的方法來改變所引用(取決於類型可以建成後改變 - 即,如果它是不可變與否)的對象。

那麼這種方法將覆蓋傳遞的數組的第一個元素:

public static void foo(string[] ss) 
{ 
    if(ss!=null && ss.Length > 0) 
    ss[0] = "Overwritten"; 
} 

public static void main() 
{ 
    var strings = new[] { String.Empty, "Original" }; 
    foo(strings); 
    Console.WriteLine(strings[0]); //will print 'Overwritten'. 
} 

foo不能在上面的代碼做什麼,但是,newss參數,並期望改變的的值參考文獻strings作爲參數從main傳遞。

爲此,我們需要傳遞一個參考strings地方,這就是ref進來。如果我們做到這一點 - 然後在main()strings可以改變,如果傳遞空,指向一個新的陣列:

public static void foo(ref string[] ss) 
{ 
    if(ss==null || ss.Length == 0) 
    ss= new string[1]; 

    ss[0] = "Overwritten"; 
} 

public static void main() 
{ 
    string[] strings = null 
    foo(ref strings); 
    Console.WriteLine(strings[0]); //will still print 'Overwritten'. 
} 
+0

我只是意識到我實際上已經忘記在我的第二個代碼塊中放入'ref'!但是,我今天起牀了十二個小時,爲其中的十一個人(還有另外五個小時睡覺前)工作,這是我的藉口。:D – 2012-03-19 17:16:47

+0

賽車傢伙的藉口,只是開個玩笑:D – 2012-03-19 17:26:25

+0

謝謝,SO社區真棒 – 2012-03-19 17:28:58

1

任何類型的在C#作爲參數傳遞由值傳遞。因此,如果這是一個字符串數組,C#將複製一個引用(這是一個引用類型)並將其傳入。但由於這是一個引用類型,因此這兩個變量(來自內部方法範圍和外部參數)都會指向同一對象位於託管堆(或大型對象堆)中。

你的代碼的問題是你創建一個新的變量(語法不正確)。爲了解決這個問題你只需要使用直接的陣列的索引,即:

void foo (string[] temp) // create a copy of a reference to the string array 
{ 
    temp[0] = "Boom"; // temp still points to the same object 
} 
------------- 
string[] temp = new [] {"one", "two", "three"}; //outer variable 
foo(temp); // behind the scene we have two variables pointing to the same array 
foreach (string s in temp) 
    System.Console.WriteLine(s); 
+0

謝謝,SO社區真棒 – 2012-03-19 17:29:04

1

思考的類引用爲「對象ID」。如果有一個類型爲Car的變量MyCar,則該變量實際上不包含汽車。相反,它保存實際存儲在別處的汽車的「對象ID」。如果MyCar持有「#1543」,則說明MyCar.Color = CarColors.Purple;實際上不會修改變量MyCar。相反,它會告訴系統「對象#1543應該是一個Car。設置其Color屬性CarColors.Purple." In many cases, a routine which passes a variable of type汽車will simply want the called code to do something with the汽車identified by that variable. In a few cases, however, one may be necessary to let the called code change the object ID stored within MyCar itself, so that it points to an entirely different instance of Car`。

你的具體情況,所涉及的對象是一個數組,被叫例程創建一個全新的數組,但調用者以temp等於null開頭,調用者看到新數組的唯一方法是如果對它的引用存儲到temp中否則調用者將繼續查看任何數組(或null,如果沒有存儲陣列參考)

+0

謝謝,SO社區真棒 – 2012-03-19 17:29:13