2013-10-09 2 views
7

Сегодня я узнал о типах значений и ссылочных типах.Строки, которые не ведут себя как ссылочный тип

Я имею одно сомнение в примере кода ниже:

class Program 
{ 
    static void Main(string[] args) 
    { 
     StringBuilder sb = new StringBuilder(); 
     FunctionSB(sb); 
     Console.WriteLine(sb); //sb updated 

     customer c = new customer(); 
     FunctionClass(c); 
     Console.WriteLine(c.s);//updated class value 

     String str = ""; 
     FuntionString(str); 
     Console.WriteLine(str);// 

    } 

    private static void FunctionSB(StringBuilder sb) 
    { 
     sb.Append("sb updated"); 
    } 

    private static void FunctionClass(customer c) 
    { 
     c.s = "updated class value "; 
    } 

    static void FuntionString(String str) 
    { 
     str = "updated value"; 
    } 
} 
class customer 
{ 
    public string s; 
} 

Здесь значение строки строитель и член класса значение переменной обновляется, но почему FuntionString(str); не обновляет значение str? (Почему он не передан в качестве ссылки?)

+0

Строка представляет собой простой тип. – Maess

+1

http://www.yoda.arachsys.com/csharp/parameters.html http://www.yoda.arachsys.com/csharp/references.html – SLaks

+3

Вы не мутируете тот же объект, что и вы, с построителем строк , вы заменяете одну ссылку на другую. Вы можете реплицировать это с помощью построителя строк, пытаясь заменить его новым экземпляром строкового построителя. – vcsjones

ответ

6

Важно различать переменную и объект .

Рассмотрим код:

String str = ""; 
FuntionString(str); 

str является переменной. Сначала значение этой переменной является ссылкой на. Предположим, что эта ссылка - номер 246.Строка 246 может быть разрешена к значению; это значение представляет собой пустой массив символов.

Затем мы передаем значение этой переменной FuntionString. Мы не передаем ссылку на переменную str, мы просто передаем номер 246. Это копия этой ссылки или номера.

Внутри этой функции есть совершенно другая переменная, которая также называется str. Тот факт, что идентификатор один и тот же, не меняет того факта, что это переменная, которая имеет одно и то же значение.

При изменении strпеременная внутри FuntionString вы не Измените str переменную из Main. После того, как тело FuntionString заканчивается, str от Main все еще держится на ссылке 246, как и раньше, а переменная str внутри FuntionString имеет значение для некоторой новой ссылки, скажем 3, строки новой строки с значение "updated value". Это изменение к переменной не отражено в Main.

В случае FunctionSB об осуществлении метода фактически не изменит sb переменную. Вместо изменения переменной он мутирует объект , на который ссылается переменная. В этом случае sb указывает на объект в определенном месте, скажем 39. sb в основном методе и в этом другом методе - это две разные переменные с копией той же ссылки. Метод не изменяет переменную sb, вместо нее изменяет объект sb в местоположении 39. Два объекта sb все еще имеют одинаковое значение; они не изменяются, но оба они указывают на один объект, который изменился. Таким образом, мутацию, выполняемую с помощью метода, можно наблюдать с Main.

Если из определения FuntionStringвы изменили объект строки, что обе переменные указывали на, а не изменять переменную самого, то изменение будет «наблюдаемым» от вызывающего абонента. Конечно, это невозможно, потому что строки неизменяемы. Метод не может мутировать объект таким образом, чтобы вызывающий мог наблюдать.

+0

Это передает то, что я пытался сказать, но лучше, так +1. – vcsjones

+0

+1 отвечает на вопрос отлично –

0

Строка в C# является встроенным типом данных. Если вы хотите сделать передать значение, так что вы можете rereference (не может изменить, только заменить строки являются неизменяемыми), вам нужно из модификатора:

static void FuntionString(out String str) 
    { 
     str = "updated value"; 
    } 

Это, по существу, такой же, как передать его по ссылке. Единственное различие заключается в том, что при использовании out ваша строка должна быть инициализирована. Если вы не можете быть уверены в этом, используйте вместо этого ref.

4

Следует понимать, что при написании str = "updated value"; вы фактически создаете новый объект. То есть, вы сделали эквивалент:

str = new string("updated value"); 

Это означает, что, когда вы пишете str = "updated value", вы присваиваете новый объект в «ул» эталонный, не изменяя существующий объект. **

Итак, правильная точка сравнения по отношению к классу «клиент» не является:

c.s = "updated class value"; 

Но скорее:

c = new customer { s = "updated class value" }. 

Исходный объект, на который указывает ссылка ранее удерживаемый «c» или «str», поэтому не изменяется.


Что нужно делать в случае ОП передать ссылку на строку, используя ref ключевое слово:

static void FuntionString(ref String str) 
{ 
    str = "updated value"; 
} 

Разница здесь в том, что сама ссылка обновляется внутри FunctionString, и теперь указывает на новый объект строки.


** Обратите внимание, что, так как .Net strings are immutable, это всегда будет правдой. Невозможно изменить строковый объект, только чтобы создать новый и повторно назначить его. Переустановить это несколько иначе: да, строка передается по ссылке, но, поскольку тип строки неизменен, вы все равно не можете использовать эту ссылку, чтобы каким-либо образом изменить объект.

+4

Я не понимаю, насколько важна неизменность строк. Следует учитывать, ссылаетесь ли вы на сам объект или просто на изменение ссылки. – vcsjones

+0

@vcsjones, потому что OP спрашивает, почему строка, являющаяся объектом, не ведет себя как «клиент» - другими словами, почему вы не можете ее изменить внутри вызова функции. Неизменность - причина, по которой вы не можете. – McGarnagle

+0

@vcsjones Я вижу, что вы имеете в виду, но изменение объекта и изменение ссылки является ключевым моментом. – McGarnagle

1

Когда вы используете StringBuilder, вы мутируете экземпляр экземпляра StringBuilder. Это тот же объект, потому что значение является ссылкой на тот же объект StringBuilder.

Рассмотрим это:

StringBuilder sb = new StringBuilder(); //sb is a variable to a string builder 

//cat is a difference reference than sb, but both values are references that point to the same string builder. 
private static void FunctionSB(StringBuilder cat) 
{ 
    cat.Append("sb updated"); 
} 

С вашей строки, например, однако, вы не изменяем тот же экземпляр строки, вы изменяете само значение будет разница ссылки.

static void FuntionString(String str) 
{ 
    str = "updated value"; 
} 

Этот вышеприведенный пример заменяет значение, которое ссылается на str. Это не уникальное поведение строки, StringBuilder ведет себя одинаково. Это имеет ту же проблему, как строки:

private static void FunctionSB(StringBuilder sb) 
{ 
    sb = new StringBuilder(); 
} 

выше не изменит строку строитель, который передается в FunctionSB. Как отмечали другие, для решения этой проблемы можно использовать ref, так как вы хотите изменить саму ссылку.

Смежные вопросы