8

那么字符串是引用类型吗?我的理解是,即使您将字符串 ByVal 传递给方法,也会传递对堆中字符串的引用。

呜呜呜……

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

我错过了什么?引擎盖下发生了什么?我敢打赌价值会改变....

4

5 回答 5

8

引用类型在 .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 从未真正改变过。指向它的变量只是更改为指向不同的字符串。

于 2011-03-02T04:09:54.400 回答
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 中,当您通过引用传递时,它并不总是清楚地表明,当某些方法意外地开始使用您的变量时,它可能会导致很多问题。

于 2011-03-02T04:14:41.030 回答
2

默认情况下,包括引用类型在内的所有类型都按值传递,如您的示例所示,这意味着传递了引用的副本。因此,无论如何,当您按值传递对象时,重新分配这样的对象将无效。您只是在更改引用的副本指向的内容。您必须通过引用显式传递才能实现您想要做的事情。

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

于 2011-03-02T04:03:08.037 回答
0

您传递的是副本而不是实际参考。

从微软阅读这篇文章

http://msdn.microsoft.com/en-us/library/s6938f28.aspx

于 2011-03-02T04:05:16.553 回答
0
  1. 当您将字符串传递给方法时,将获取引用的副本。因此,Value是一个全新的变量,它恰好仍然引用内存中的相同字符串。
  2. 字符串文字也被"test"创建为真正的引用类型对象。它不仅仅是源代码中的一个值。
  3. 当您分配"test"给 时Value,您的Value变量的引用将更新为引用"test"而不是原始字符串。由于这个引用只是一个副本(正如我们在步骤 1 中看到的),myTestValue函数外部的变量保持不变,仍然引用原始字符串。

You can get a better understanding of this by testing on a type with a property you can update. If you make a change to just the property, that change is visible outside the function. If you try to replace the entire object (as you are doing with this string), that is not visible outside the function.

于 2011-03-02T04:23:53.290 回答