在VB.NET和C#中传递字符串ByVal#
所以字符串是引用类型的权利?我的理解是,即使将字符串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
我缺少什么?那么发生了什么?我会赌我的生命值会改变....
参考类型在.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从未真正改变过。指向它的变量刚刚更改为指向不同的字符串。
+1。字符串与这种方式没有任何其他类型不同。 – 2011-03-02 04:20:38
所有不错的内容,但是tl; dr – 2011-03-02 04:29:34
@Joel Coehoorn:谢谢。我不希望你的才能的人阅读整个事情。希望对于那些提出这个问题的人来说,这很简单。随意编辑它认为不必要的罗嗦的任何部分。 – StriplingWarrior 2011-03-02 05:55:39
所有类型,包括引用类型是按值默认情况下,在你的榜样传递,因为,这意味着的参考副本已通过。所以,无论如何,重新赋值这样的对象在按值传递时都不起作用。您只需更改副本的参考点即可。你必须明确地通过引用来实现你想要做的事情。
只有当您通过修改时,通过值传递的对象才能在方法外看到该效果。当然,字符串是不可变的,所以在这里并不适用。
字符串是引用并存储在堆中,而不是对问题有任何影响。关于字符串的存储方式没有任何特殊之处,只是它们是唯一的也可以是字面值的引用类型。 – 2011-03-02 04:19:37
你说得对,谢谢。 – 2011-03-02 04:27:31
很好的编辑。将-1更改为+1。 – StriplingWarrior 2011-03-02 05:56:32
除非另有说明,否则所有内容均按值传递。当你传递一个字符串时,你实际上是通过值传递一个引用。
对于字符串,这并没有太大的差别,因为字符串是不可变的。意思是你永远不会修改你收到的字符串。但对于其他类,您可以修改通过值传递的对象(除非像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中,当你通过引用传递时,它并不总是很清楚,当某些方法意外地开始使用变量时,它可能会导致很多问题。
- 当您将该字符串传递给该方法时,将采用副本的引用。因此,
Value
是一个全新的变量,它恰好仍然指向内存中的同一个字符串。 -
"test"
字符串文字也创建为真实的引用类型对象。这不仅仅是源代码中的一个值。 - 当您指定
"test"
到Value
,为您Value
变量的引用更新为参考"test"
代替原来的字符串。由于该引用只是一个副本(正如我们在步骤1中看到的),函数外部的myTestValue
变量保持不变,仍然引用原始字符串。
您可以通过测试具有可更新属性的类型来更好地理解此问题。如果您仅对属性进行更改,则该更改在该函数外部可见。如果你试图替换整个对象(就像你正在处理这个字符串一样),那在函数外部是不可见的。
我推荐阅读这篇文章:http://www.yoda.arachsys.com/csharp/parameters.html和重复问题的答案。 – 2011-03-02 04:04:57
哇,我完全不明白这一点。 “myTestValue”怎么可能改变?它没有被修改?你觉得你怎么修改它? – Stan 2011-03-02 04:07:52
@Stan:人们非常(非常)经常混淆传递引用和传递*引用类型*的值。 – 2011-03-02 04:26:35