VB.NET:如果我将一个字符串以ByVal方式传递到函数中,但不改变该字符串,那么在内存中有一个或两个字符串?

7

我知道字符串是不可变的,所以一旦你改变一个字符串引用的值,.NET会在堆上创建一个全新的字符串。

但是如果你没有改变字符串引用的值,而只是将它作为ByVal传递到函数中,这个操作是否也会在堆上复制字符串的值?我的倾向是“不会”,但我想确认一下。

例如:

Public Function IsStringHello(ByVal test As String) As Boolean 
  Return (String.Compare(test, "Hello") = 0)    
End Function

调用程序:

Dim myWord as String = "Blah"
Dim matchesHello as Boolean = IsStringHello(myWord)

我知道通过值传递myWord会复制对“Blah”的引用,但是由于我没有尝试更改字符串本身,它会在堆上再次复制字符串吗?


为什么人们要用这种奇怪的方式比较字符串呢?...https://dev59.com/-XRA5IYBdhLWcg3wsgLq#859078 - Dario
1
使用String.Compare可以指定大小写敏感性。这可能比在比较之前将字符串转换为大写更清晰。 - Rob Sobers
6个回答

9
顺便提一下,字符串驻留与此完全无关。对于所有引用类型(实际上是所有类型),无论它们如何在内部管理,传递参数到函数的规则都是相同的。
规则很简单,你已经正确地陈述了:按值传递会复制“引用”,而不是“目标”。这里不会复制堆空间。

字符串的intern()方法并不直接相关。你说:“对于所有引用类型(实际上,所有类型),无论它们如何在内部管理,规则都是相同的——我认为你是针对引用的复制而不是interning说的,对吗? - shahkalpesh

4

不,它仍然使用引用的副本到"Blah"。
你认为它会怎样呢?

此外,字符串是被存储在池中的。

string s = "hello";
string t = "hello";

s和t都指向同一个字符串(因为它是内部化的)。如果你修改s或t,那么它将创建一个新的字符串。


我有一个奇怪(愚蠢?)的想法,也许仅仅复制不可变对象的引用就会导致对象本身的复制。我强烈怀疑这不是事实,但我想要确认一下。关于字符串“interning”的伟大附注——我不知道这个,谢谢! - Rob Sobers
字符串常量被池化。通过其他方式创建的字符串不会被池化。 - Jon Skeet
@JonSkeet - 啊哈...所以在shahkalpesh上面的答案中,当我们将两个引用分配给相同的字符串字面值时,并不意味着它们引用堆上的同一字符串? - Rob Sobers
1
@Jon:如果字符串s =“hello”; 字符串t =“ello”; 字符串z =“h”+ t; 那么s和z指向同一实例吗? - shahkalpesh
什么是internedf的意思? - user4234
@SharenEayrs:请参见https://msdn.microsoft.com/zh-cn/library/system.string.intern(v=vs.110).aspx。另外,https://en.wikipedia.org/wiki/String_interning。 - shahkalpesh

2

通过按值传递对象会创建指针的副本,而不是对象本身。下面是一个演示:

Module Module1
    Dim original As String = "Hello world"

    Sub PassByReferenceTest(ByVal other As String)
        Console.WriteLine("object.ReferenceEquals(original, other): {0}", _
            Object.ReferenceEquals(original, other))
    End Sub

    Sub Main()
        PassByReferenceTest(original)
        Console.ReadKey(True)
    End Sub
End Module

该程序输出以下内容:
object.ReferenceEquals(original, other): True

因此,原始字符串和我们通过值传递的字符串存在于同一个内存地址。您并没有复制字符串本身。


0
简而言之,不会。它只传递字符串的引用,而不是字符串本身的多个实例。

0

string是一个引用类型。如果你按值传递它,你传递的是引用的值。

唯一能在堆上得到另一个副本的方法是改变变量的值。


0

类型为System.String的变量实际上保存了一个“对象ID”。假设Object #1934是一个包含字符"Blah"的字符串,你说Dim myWord As String = "Blah"。编译器将把Object #1934存储到myWord中。调用IsStringHello(myWord)将导致该函数被调用,其test参数等于Object #1934。在您的示例中,内存中将有两个类型为System.String的变量--myWordtest,并且两者都将保存内容Object #1934


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接