为什么我在一个函数中发送一个字符串作为参数它返回空?

时间:2012-09-05 20:01:56

标签: c# .net

使用此代码:

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

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

 }

当我使用刚在主子例程中创建的简单字符串调用此方法时:

 string localString = "";
 SomeMethod( localString );

我在localString中返回的值是一个空事件,当我在调试中看到方法SomeMethod中变量tmpStr不为空且变量{{1}时在方法中充满了chars。

如果我将str签名更改为:

SomeMethod

我看到了正确的结果。

但是字符串是一种引用类型,所以为什么在这种情况下我需要用SomeMethod(ref string str) 发送它? 以及为什么只有在我打电话时才能得到我预期的结果:

ref

4 个答案:

答案 0 :(得分: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时,它会修改引用str,但不会修改localString

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

因此,您对str的修改只更改了str引用的内容,而不是原始引用localString,如果您想要更改那个,那么您通过通过引用,这意味着str是对原始参数的引用(很像ptr到ptr):

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

现在,当您更改str时,它也会更改引荐localString

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

然后,当然,原始字符串(假设在本例中没有其他任何引用它)可以被垃圾收集......

因此,如果您确实要修改string参数,请按refout传递,或者您可以返回新的变异版本,或存储在实例成员中(虽然pass-by-instance-member是一个更高的耦合顺序,可能会导致其他问题......)。

答案 1 :(得分:2)

如果你没有使用ref所有的更改,但是会发生assign,这是因为方法体内的str是作为参数传递的变量的副本,它指向同一个实例内存所以如果你调用方法或访问字段它是在同一个实例上,但是如果你将另一个实例分配给str它只开始指向另一个实例。但由于str只是用作参数的变量的副本,因此参数不会被更改。如果使用ref,则不会创建任何副本并使用相同的指针。

答案 2 :(得分:1)

这应该做你想要的:

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

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

         return str;
}

string localString = SomeMethod();

答案 3 :(得分:1)

“你不能改变引用本身的值;也就是说,你不能使用相同的引用为新类分配内存并让它在块之外保持不变。”

从这个网站: http://msdn.microsoft.com/en-us/library/0f66670z(v=vs.71).aspx#vclrfpassingmethodparameters_example4

所以你只是改变了块内的引用。有两种方法可以解决这个问题。 1.通过您发现的ref关键字传递。 2.正如其他人所说的那样;更改方法以返回一个字符串,然后像这样声明你的新字符串:

String localString = SomeMethod();