VBA字符串是否不可变?

10

我知道 .NET (因此也包括 VB.NET) 字符串是不可变的。然而,我正在使用 Excel 2010 中的 VBA 7.0。那里的字符串是不可变的吗?我正在进行大量字符串处理,对于小数量的字符串直接操作还可以,但我担心它无法扩展 - 因为从一个字符串移动到另一个字符造成的每个额外字符可能会创建另一个字符串实例。

3个回答

9

它们是不可变的,除非它们表现出可变行为,那么它们就不是不可变的。

例如,通过mid$()进行的赋值比普通的新字符串赋值要快得多。

Dim s As String

s = "ABC"

Debug.Print s, StrPtr(s)

'// -> ABC            122899836

Mid$(s, 1, 1) = "Z"

Debug.Print s, StrPtr(s)

'// -> ZBC            122899836     

s = "??" & Right$(s, 1)

Debug.Print s, StrPtr(s)

'// -> ??C            196635748 

+1 我刚想说 Dim str As String: str = "this string": Debug.Print VarPtr(str), StrPtr(str): Mid(str, 3, 2) = "at": Debug.Print VarPtr(str), StrPtr(str) - user2140173

5

虽然 VB.NET 字符串是不可变的,正如 System.String 所要求的那样,但 VBA(包括 VB6?)字符串可以被改变(例如使用 Mid$)。请参见 Alex K's answer 并注意操作后的 StrPtr 结果。


原始答案; 支持文档,反对反例。

VBA字符串是不可变的。

就像VB.NET一样,没有办法在不创建新字符串的情况下“替换部分”或“追加到”字符串。这是否重要 - 因为现代计算机非常快 - 取决于实际算法、数据和环境。


与.NET文档不同,追踪VBA的此类行为参考变得越来越困难。从MS-VBAL:2.1数据值和值类型中,我们发现这个罕见的小宝石

单个数据值是不可变的。这意味着在VBA环境中没有定义机制可以将一个数据值更改为另一个数据值。

其中字符串表示“单个数据值”。


1
这个答案是错误的,但我同情发帖者,因为即使在发表此评论的日期,微软仍然声称它们是不可变的。https://learn.microsoft.com/en-us/dotnet/visual-basic/programming-guide/language-features/strings/string-basics -- 微软说疯狂的话!请看我的上一个回答。 - www-0av-Com
1
@www-0av-Com 您的链接是针对 VB.NET 的。它正确地说字符串是不可变的,因为在 .NET 中它们是不可变的。VBA 是一种独立的语言。 - GSerg

0

VBA字符串是可变的。Alex K的答案是错误的,但他在Microsoft的VB.Net中关于Mid()=的欺骗是正确的,后者正确地声称它们是不可变的--https://learn.microsoft.com/en-us/dotnet/visual-basic/programming-guide/language-features/strings/string-basics--尽管支持Mid()=

因此,在所有VB6、VBA、VB.Net中,您可以使用(例如)Mid(a$,1,2)="hi",如Alex的回复所述,来覆盖字符串的部分。然而,正如user2864740指出的那样,只有VB6和VBA能够非常快速地执行此操作。这是VB6(VBA)和VB.Net中经过测试的代码。

Dim n1&, start!, bigstring$
bigstring$ = Space(10 ^ 6)
start = timer() ' in vb.Net this is function with "timer = (DateTime.Now.Ticks - Today.Ticks) / 10000000.0#"

For n1 = 1 To 5000
    Mid(bigstring, n1, 1) = "1"
Next

Debug.Print("Elapsed millisecs: " & (timer() - start!) * 1000.0! & " over cycle count of " & n1 - 1)

在超过1兆字节的大字符串上,这种技术对于速度至关重要,因此可扩展。在VBA中,它可以在不到一毫秒的时间内完成。但是,在VB.Net中,效率非常低下--同样的测试在3GHz CPU上需要7秒,每次迭代约为1毫秒。从5000次迭代增加到100万次迭代后,VBA仍然只需要40毫秒。VB.Net需要30分钟。提示:在VB.Net中,您可以使用Dim BigByte() as byte,然后BigByte = System.Text.Encoding.UTF8.GetBytes(bigstring),以便您使用一个字节数组。如何使其灵活和支持Unicode超出了本主题。


3
VB.NET利用一种小“技巧”来重新分配变量,使其看起来字符串已经改变。原始字符串并没有改变。例如:Dim a$ = "Hello!" ; Dim b$ = a$ ; Console.WriteLine(ReferenceEquals(a$, b$)) ; MID(a$, 1) = "Goodbye" ; Console.WriteLine(ReferenceEquals(a$, b$))。这会显示“True, False”(因为使用MID函数会将a$重新分配到一个_新的_字符串上,同时保留原始字符串不变)。无论使用哪种语言,.NET中的System.String类型都是不可变的。我没有在VB6/VBA中确认这一点。 - user2864740
1
您的参考链接是针对VB.NET的,其中字符串是不可变的,因为在.NET中原则上它们是不可变的,所以该文档是正确的。在VBA中,由于没有这样的限制,字符串是可变的。 - GSerg
1
关于“因此,在所有VB6、VBA、VB.Net中…”。无论如何,我已经更新了我的答案(不是这个答案),以反映对于Alex K的VBA/VB6行为的回应。 - user2864740
感谢大家的更新。你们三个救了我一场大难,因为我正要将一个使用Mid()=来提高速度的VB6 Web服务器迁移到VB.Net。我需要做比最初预想的更大的改变。 - www-0av-Com
@user2864740,既然我帮助你纠正了答案,还给你点了赞,并且为这个问题增加了很多深度,那么能不能点个赞呢?我不是追求荣誉的人,但有些事情我必须达到一定的水平才行。这不就是“以牙还牙”吗? :) - www-0av-Com

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