2012-09-05 21 views
3

有了這個代碼:爲什麼如果我在函數中發送一個字符串作爲參數,它會回到空白?

void SomeMethod(string str) 
{ 
     string tmpStr = ... Read some value from file ... 

     if(!tmpStr.Empty()) 
      str = tmpStr; 

} 

當我把這種方法只用主子程序內創建了一個簡單的字符串:

string localString = ""; 
SomeMethod(localString); 

,我取回localString值是空的事件時,我在調試中看到,在方法SomeMethod中,變量tmpStr不是空的,並且該方法中的變量str充滿了字符。

如果我改變SomeMethod簽名是:

SomeMethod(ref string str) 

我看到正確的結果。

但字符串是一個引用類型,所以爲什麼我需要在這種情況下發送它與ref? 爲什麼我得到的結果,我預計只有當我打電話:

SomeMethod(string str) 
+6

此行爲是預期的,字符串是不可變的設計。看看這個:http://stackoverflow.com/questions/636932/in-c-why-is-string-a-reference-type-that-behaves-like-a-value-type –

+3

什麼是Empty() '?我正在假設某種擴展方法? – Oded

+0

你有沒有嘗試使返回類型爲字符串? – tehdoommarine

回答

7

這不只是該字符串是不可改變的,而且,雖然string是引用類型,引用本身不是。也就是說,您通過引用傳遞string,但引用本身是按值傳遞的。因此,對於引用類型,您可以修改參數所引用的對象(只要它是可修改的),但除非通過引用傳遞它,否則不能修改參數引用的內容。

所以,當你試圖改變string變量指的是什麼:

str = tmpStr; 

它改變了什麼str是指在本地,但不影響什麼原始的參數localString指。

它認爲這種方式,讓我們說,爭論localString是指物體在位置1000:

localString 
+---------------+      1000 
|  1000  | -----------------> +---------------+ 
+---------------+      | Count: 1  | 
             | Value: ""  | 
             +---------------+ 

然後當我們通過localString的方法,它會創建引用的副本(如str )並更新引用計數...

localString 
+---------------+      1000 
|  1000  | -----------------> +---------------+ 
+---------------+      | Count: 2  | 
             | Value: ""  | 
str         +---------------+ 
+---------------+      ^
|  1000  | ---------------------+ 
+---------------+ 

然後,當你指派海峽到一個新的字符串,它修改參考str但不localString

localString 
+---------------+      1000 
|  1000  | -----------------> +---------------+ 
+---------------+      | Count: 1  | 
             | Value: ""  | 
str         +---------------+ 
+---------------+       2500 
|  2500  | ---------------------> +---------------+ 
+---------------+       | Count: 1  | 
              | Value: ... | 
              +---------------+ 

所以你的str修改不僅改變是指,不是原來的refernce什麼strlocalString,如果你想改變,那麼你通過引用傳遞,這意味着str是一個引用回到原始參數(很像ptr到ptr):

localString 
+---------------+      1000 
|  2500  | ------------------> +---------------+ 
+---------------+      | Count: 2  | 
     ^       | Value: ""  | 
str  |        +---------------+ 
+---------------+  
|    |  
+---------------+                

現在,當你改變str它改變了refernce localString還有:

localString 
+---------------+      1000 
|  1000  | -----+    +---------------+ 
+---------------+  |    | Count: 0  | 
     ^    |    | Value: ""  | 
str  |    |    +---------------+ 
+---------------+  |     2500 
|    |  +----------------> +---------------+ 
+---------------+       | Count: 1  | 
              | Value: ... | 
              +---------------+ 

然後,當然,原始字符串(假設沒有其他物質是指它在這個例子中)可垃圾回收......

所以,如果你真的想修改string參數,通過refout通過它,或者你可以返回新的突變形式,或以一個實例會員商店(雖然傳遞的實例 - 成員是更高的耦合順序,並可能導致其他問題......)。

1

這應該做你想要什麼:

string SomeMethod() 
{ 
     string tmpStr = ... Read some value from file ... 

     if(!tmpStr.Empty()) 
      str = tmpStr; 

     return str; 
} 

string localString = SomeMethod(); 
+0

好的,但是在'SomeMethod'中兩個字符串作爲參數的情況呢?我不想返回一個類/結構作爲返回值。 – Yanshof

2

如果不使用REF所有的變化,但分配會發生,這是因爲該方法的身體內str是副本變量作爲參數傳遞,它指向內存中的同一實例,因此如果您調用方法或訪問字段,它將位於同一個實例上,但是如果您將另一個實例指定爲str,則只會將其指向另一個實例。但是因爲str只是用作參數的變量的副本,所以參數不會被改變。如果您使用ref,則不會創建副本,並使用相同的指針。 「

1

」你不能改變引用本身的值;也就是說,你不能使用相同的引用來爲新類分配內存,並讓它保持在塊之外。

從這個網站: http://msdn.microsoft.com/en-us/library/0f66670z(v=vs.71).aspx#vclrfpassingmethodparameters_example4

所以你只改變了塊內的參考。有兩種解決方法。 1.通過您發現的ref關鍵字進行傳遞。 2.其他人建議;更改方法返回一個字符串,然後像這樣聲明新的字符串:

String localString = SomeMethod(); 
相關問題