2011-03-02 19 views
6

所以字符串是引用类型的权利?我的理解是,即使将字符串ByVal传递给方法,也会传递堆中字符串的引用。在VB.NET和C#中传递字符串ByVal#

的sooo .....

String myTestValue = "NotModified"; 
TestMethod(myTestValue); 
System.Diagnostics.Debug.Write(myTestValue); /* myTestValue = "NotModified" WTF? */ 

private void TestMethod(String Value) 
{ 
    Value = "test1"; 
} 

或者

Dim myTestValue As String = "NotModified" 
TestMethod(myTestValue) 
Debug.Print(myTestValue) /* myTestValue = "NotModified" WTF? */ 

Private Sub TestMethod(ByVal Value As String) 
    Value = "test1" 
End Sub 

我缺少什么?那么发生了什么?我会赌我的生命值会改变....

+0

我推荐阅读这篇文章:http://www.yoda.arachsys.com/csharp/parameters.html和重复问题的答案。 – 2011-03-02 04:04:57

+0

哇,我完全不明白这一点。 “myTestValue”怎么可能改变?它没有被修改?你觉得你怎么修改它? – Stan 2011-03-02 04:07:52

+1

@Stan:人们非常(非常)经常混淆传递引用和传递*引用类型*的值。 – 2011-03-02 04:26:35

回答

7

参考类型在.NET通过 “按值参考”。这意味着,实际参数分配一个不同的值实际上并不改变原来的值(除非你使用的ByRef/REF)。但是,如果您要更改传入的实际对象,将会更改调用方法引用的对象。例如,考虑下面的程序:

void Main() 
{ 
    var a = new A{I=1}; 
    Console.WriteLine(a.I); 
    DoSomething(a); 
    Console.WriteLine(a.I); 
    DoSomethingElse(a); 
    Console.WriteLine(a.I); 
} 

public void DoSomething(A a) 
{ 
    a = new A{I=2}; 
} 

public void DoSomethingElse(A a) 
{ 
    a.I = 2; 
} 

public class A 
{ 
    public int I; 
} 

输出:

1 
1 
2 

DoSomething方法将它的a参数具有不同的值,但该参数仅仅是一个局部指针的位置来自调用方法的原始a。改变指针的值没有做任何改变调用方法的a值。然而,实际上DoSomethingElse做了更改,以引用的对象上的一个值。

不管其他应答者说什么,string是不能以这种方式特殊。所有对象的行为都是这样。

string许多对象不同的是,这是不可改变的:有不在,你可以打电话到真正改变串串任何方法或属性或字段。一旦字符串在.NET中创建的,它是只读的。

当你做这样的事情:

var s = "hello"; 
s += " world"; 

...编译器开启此弄成这个样子:

// this is compiled into the assembly, and doesn't need to be set at runtime. 
const string S1 = "hello"; 
const string S2 = " world"; // likewise 
string s = S1; 
s = new StringBuilder().Append(s).Append(S2).ToString(); 

最后一行产生一个新的字符串,而S1和S2仍在游逛。如果它们是组件中的常量字符串,它们将停留在那里。如果它们是动态创建的并且没有更多的引用,那么垃圾收集器可以取消引用它们来释放内存。但关键是要认识到S1从未真正改变过。指向它的变量刚刚更改为指向不同的字符串。

+0

+1。字符串与这种方式没有任何其他类型不同。 – 2011-03-02 04:20:38

+0

所有不错的内容,但是tl; dr – 2011-03-02 04:29:34

+0

@Joel Coehoorn:谢谢。我不希望你的才能的人阅读整个事情。希望对于那些提出这个问题的人来说,这很简单。随意编辑它认为不必要的罗嗦的任何部分。 – StriplingWarrior 2011-03-02 05:55:39

2

所有类型,包括引用类型是按值默认情况下,在你的榜样传递,因为,这意味着参考副本已通过。所以,无论如何,重新赋值这样的对象在按值传递时都不起作用。您只需更改副本的参考点即可。你必须明确地通过引用来实现你想要做的事情。

只有当您通过修改时,通过值传递的对象才能在方法外看到该效果。当然,字符串是不可变的,所以在这里并不适用。

+1

字符串是引用并存储在堆中,而不是对问题有任何影响。关于字符串的存储方式没有任何特殊之处,只是它们是唯一的也可以是字面值的引用类型。 – 2011-03-02 04:19:37

+0

你说得对,谢谢。 – 2011-03-02 04:27:31

+0

很好的编辑。将-1更改为+1。 – StriplingWarrior 2011-03-02 05:56:32

3

除非另有说明,否则所有内容均按值传递。当你传递一个字符串时,你实际上是通过值传递一个引用。

对于字符串,这并没有太大的差别,因为字符串是不可变的。意思是你永远不会修改你收到的字符串。但对于其他类,您可以修改通过值传递的对象(除非像String一样,它是不可变的)。你不能做什么,以及通过引用传递什么让你做,是修改变量你正在通过。

例子:

Public Class Example 
    Private Shared Sub ExampleByValue(ByVal arg as String) 
     arg = "ByVal args can be modifiable, but can't be replaced." 
    End Sub 

    Private Shared Sub ExampleByRef(ByRef arg as String) 
     arg = "ByRef args can be set to a whole other object, if you want." 
    End Sub 

    Public Shared Sub Main() 
     Dim s as String = "" 
     ExampleByValue(s) 
     Console.WriteLine(s) ''// This will print an empty line 
     ExampleByRef(s) 
     Console.WriteLine(s) ''// This will print our lesson for today 
    End Sub 
End Class 

现在,这应该是非常谨慎使用,因为值是默认和预期。特别是在VB中,当你通过引用传递时,它并不总是很清楚,当某些方法意外地开始使用变量时,它可能会导致很多问题。

0
  1. 当您将该字符串传递给该方法时,将采用副本的引用。因此,Value是一个全新的变量,它恰好仍然指向内存中的同一个字符串。
  2. "test"字符串文字也创建为真实的引用类型对象。这不仅仅是源代码中的一个值。
  3. 当您指定"test"Value,为您Value变量的引用更新为参考"test"代替原来的字符串。由于该引用只是一个副本(正如我们在步骤1中看到的),函数外部的myTestValue变量保持不变,仍然引用原始字符串。

您可以通过测试具有可更新属性的类型来更好地理解此问题。如果您仅对属性进行更改,则该更改在该函数外部可见。如果你试图替换整个对象(就像你正在处理这个字符串一样),那在函数外部是不可见的。