Передача строки ByVal в VB.NET и C#
Так что строки являются ссылочными типами, верно? Насколько я понимаю, ссылка на строку в куче передается, даже когда вы передаете строку 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
Что мне не хватает? А что происходит под капотом? Я бы поспорил в своей жизни, что значение изменилось бы....
5 ответов
Ссылочные типы передаются "ссылка по значению" в.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 фактически никогда не изменялся. Переменная, указывающая на нее, просто изменилась, чтобы указать на другую строку.
Все передается по значению, если не указано иное. Когда вы передаете строку, вы фактически передаете ссылку по значению.
Для строк это не имеет большого значения, поскольку строки неизменяемы. Это означает, что вы никогда не сможете изменить полученную строку. Однако для других классов вы можете изменить объект, переданный по значению (если, как и 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
переменная вне функции остается неизменной и по-прежнему ссылается на исходную строку.
Вы можете лучше понять это, протестировав тип со свойством, которое вы можете обновить. Если вы вносите изменение только в свойство, это изменение будет видно за пределами функции. Если вы попытаетесь заменить весь объект (как вы делаете с этой строкой), это не будет видно за пределами функции.
Вы передаете копию, а не фактическую ссылку.
прочитайте эту статью от Microsoft