Строковый объект действительно по ссылке?
Я изучаю (новичок).NET, и у меня есть некоторые сомнения.
Чтение из примеров книг Я узнал, что String - это объект, а затем Тип ссылки.
Итак, я сделал этот тест, и результат был другим, что я ожидал:
Мне действительно интересно, это исключение, потому что "строка" - это специальные типы?
class Program
{
static void Main(string[] args)
{
SByte a = 0;
Byte b = 0;
Int16 c = 0;
Int32 d = 0;
Int64 e = 0;
string s = "";
Exception ex = new Exception();
object[] types = { a, b, c, d, e, s, ex };
// C#
foreach (object o in types)
{
string type;
if (o.GetType().IsValueType)
type = "Value type";
else
type = "Reference Type";
Console.WriteLine("{0}: {1}", o.GetType(), type);
}
// Test if change
string str = "I'll never will change!";
Program.changeMe(str);
Console.WriteLine(str);
}
public static string changeMe(string param)
{
param = "I have changed you!!!";
return ""; // no return for test
}
}
Вывод:
System.SByte: Value type
System.Byte: Value type
System.Int16: Value type
System.Int32: Value type
System.Int64: Value type
System.String: Reference Type
System.Exception: Reference Type
I'll never will change!
Ответы
Ответ 1
Строка действительно является ссылочным типом. Однако, когда ваш основной метод вызывает changeMe (str),.NET передает копию ссылки на str для измененияMe в аргументе param. changeMe затем модифицирует эту копию, чтобы ссылаться на "Я изменил вас!!!", но исходная ссылка на str все еще указывает на "Я никогда не изменюсь".
В качестве ссылочного типа означает, что если вы изменили состояние переданной строки, вызывающий элемент увидит эти изменения. (Вы не можете сделать это для строки, потому что строки неизменяемы, но вы можете сделать это для других ссылочных типов, например Control.) Но переназначение параметра не изменяет значение, переданное вызывающим в этом параметре, даже если это значение равно ссылку.
Ответ 2
Строки - это ссылки.
changeMe
не изменяет строку, он просто переназначает локальную ссылку (указатель) внутри этой функции.
Теперь, если вы передали строку как ref
arg, вы можете получить больше удовольствия:
public static string changeMe(ref string param) {
param = "I have changed you!!!";
return ""; // no return for test
}
Ответ 3
Это не имеет ничего общего со строкой, являющейся типом значения или ссылочным типом. Параметр вашего метода changeMe не помечен ref
, поэтому, если метод изменит его, вызывающий объект не увидит изменения.
Попробуйте вместо этого:
public static string changeMe(ref string param)
{
param = "I have changed you!!!";
return ""; // no return for test
}
Ответ 4
Причина, по которой Program.changeMe(str) не вызывает str для установки "Я изменил вас!!!" что хотя строка является ссылочным типом, ссылки передаются по значению.
Итак, param - это копия ссылки, она изменяется внутри области Program.changeMe(param) и затем отбрасывается в конце метода.
Ссылка str только копируется, не изменяется на ссылку "Я изменил вас!!!"
Ответ 5
Строки действительно являются особым случаем. Они являются ссылочным типом, но ведут себя как типы значений. Это было сделано для "простоты" командой разработчиков .Net.
Отличная цитата:
Это ссылочный тип
Это распространенное заблуждение, что строка - это тип значения. Это потому, что его неизменность (см. Следующий пункт) заставляет его действовать как тип значения. Он фактически действует как обычный ссылочный тип. Дополнительные сведения о различиях между типами значений и ссылочными типами см. В моих статьях о передаче параметров и памяти.
От: http://www.yoda.arachsys.com/csharp/strings.html
Ответ 6
Страница MSDN может помочь объяснить, почему это ссылочный тип.
Поскольку String
представляет собой набор из char
и наследуется от различных базовых классов Collection, это класс и, следовательно, ссылочный тип.
Ответ 7
Да, это кажется странным. Происходит то, что вы передаете ссылку на свою строку в параметре, но вы передаете ее по значению, т.е. Вы создаете копию ссылки в параметре. Поэтому, когда вы переназначаете его в рамках своей функции, вы меняете локальную копию метода changeMe() вашей строковой ссылки.
Больше информации об этом здесь:
http://www.yoda.arachsys.com/csharp/parameters.html
Ответ 8
Вы передали по значению и сделали копию ссылки. Передайте его по ссылке, он изменится. Ну, действительно, новая строка будет создана, и ваша ссылка будет на новое место.
Ответ 9
Если вы планируете изменить значение аргумента на метод, вы должны действительно использовать ключевые слова "ref" или "out". В противном случае вы рискуете запутать методы, которые могут или не могут делать то, что вы намереваетесь. Как указывали другие, вы меняете значение ссылки на строку, а не экземпляр объекта. Чтобы увидеть, как это работает с другими классами, я написал быстрый пример
static void Main(string[] args)
{
var str = "String";
var obj = new MyObject() { Value = "Object" };
Console.WriteLine(str); //String
Console.WriteLine(obj); //Object
ChangeMe(str);
ChangeMe(obj);
Console.WriteLine(str); //String
Console.WriteLine(obj); //Object
ChangeMeInner(obj);
// this is where it can get confusing!!!
Console.WriteLine(obj); //Inner
ChangeMe(ref str);
ChangeMe(ref obj);
Console.WriteLine(str); // Changed
Console.WriteLine(obj); // Changed
Console.Read();
}
class MyObject
{
public string Value { get; set; }
public override string ToString()
{
return Value;
}
}
static void ChangeMe(MyObject input)
{
input = new MyObject() { Value = "Changed" };
}
static void ChangeMeInner(MyObject input)
{
input.Value = "Inner";
}
static void ChangeMe(string input)
{
input = "Changed";
}
static void ChangeMe(ref MyObject input)
{
input = new MyObject() { Value = "Changed" };
}
static void ChangeMe(ref string input)
{
input = "Changed";
}
Ответ 10
Для всех других ссылочных типов передача его с использованием семантики byval (без ref или out) означает, что вы не можете изменить, какой объект находится в куче, на которую ссылаются точки вызова вызывающих, это не означает, что вы не можете изменить содержимое объекта на куче, на которую указывает переменная. Поскольку строки являются неизменными, они действительно являются исключением, они ведут себя как тип значения в этом отношении, хотя они являются ссылочным типом, но это делается с помощью исключительного дополнительного кодирования в среде CLR.