Проход по значению и передача по ссылке - PullRequest
5 голосов
/ 26 мая 2010

Не могли бы вы объяснить следующее поведение класса C #. Я ожидаю classResult как "Class Lijo"; но фактическое значение «Изменено».

Мы делаем копию ссылки. Хотя копия указывает на тот же адрес, метод, получающий аргумент, не может изменить оригинал.

Тем не менее, почему значение изменяется?

public partial class _Default : Page 
{
    protected void Page_Load(object sender, EventArgs e)
    {
        String nameString = "string Lijo";

        Person p = new Person();
        p.Name = "Class Lijo";

        Utilityclass.TestMethod(nameString, p);
        string classResult = p.Name;
        Response.Write(nameString + "....." + classResult);
    }
}

public class Utilityclass
{
    public static void TestMethod(String nameString, Person k)
    {
        nameString = "Changed";
        k.Name = "Changed";
    }
}

public class Person
{
    public string Name
    {
        get; set;
    }
}

Обновление : Когда я передаю строку, она фактически не изменяется.

Ответы [ 6 ]

22 голосов
/ 26 мая 2010

Самый краткий ответ: прочитайте мою статью о передаче параметров , в которой достаточно подробно.

Немного более длинный ответ - сравнить эти два метода, каждый из которых использует значение параметры:

public void ChangeMe(string x)
{
    x = "changed";
}

public void ChangeMe(Person x)
{
    x.Name = "changed";
}

В первом случае вы изменяете значение параметра . Это полностью изолировано от первоначального аргумента. Вы не можете изменить содержимое самой строки, потому что строки неизменны.

Во втором случае вы изменяете содержимое объекта, значение параметра которого относится к . Это не меняет значение самого параметра - это будет та же самая ссылка. Например, если кто-то доставляет в ваш дом что-то, что меняет содержимое вашего дома, но не меняет адрес вашего дома.

Если вы изменили второй метод на этот:

public void ChangeMe(Person x)
{
    x = new Person("Fred");
}

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

Теперь, когда вы используете параметр ref, переменная, используемая вызывающей стороной в качестве аргумента, «связывается» с параметром - так что если вы измените значение параметра, это также изменит значение аргумента , Так что, если мы изменим последний метод следующим образом:

public void ChangeMe(ref Person x)
{
    x = new Person("Fred");
}

, то:

Person y = new Person("Eric");
ChangeMe(ref y);
Console.WriteLine(y.Name);

это выведет "Fred".

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

7 голосов
/ 26 мая 2010

Person является ссылочным типом, поэтому независимо от того, используете ли вы ref, out или ничего, вы всегда сможете изменить его внутри метода. Вы никогда не передаете объект реального человека методу, вы передаете указатель как ссылку, но не фактический Person. Ключевое слово ref полезно для типов значений (таких как struct, int, float, DateTime, ...). Он также может использоваться со ссылочными типами, но только для указания поведения, но не может его принудительно применить. Если вы используете его со ссылочными типами, это позволяет вам изменить объект, на который указывает эта ссылка.

0 голосов
/ 26 мая 2010

На этот вопрос в основном ответили, но я думаю, что вы могли бы попробовать этот фрагмент (бонусные баллы, если вы попробуете это с целыми!)

class Program
{
    static void Main(string[] args)
    {            
        Person p = new Person();
        p.Name = "Class Lijo";

        Utilityclass.TestMethod(p);
        string classResult = p.Name;
        Console.WriteLine(classResult);
        Utilityclass.TestMethod2(ref p);
        classResult = p.Name;  // will bomb here           
        Console.WriteLine(classResult);
    }
}

public class Utilityclass
{
    public static void TestMethod(Person k)
    {
        k.Name = "Changed";
        k = null;
    }

    public static void TestMethod2(ref Person k)
    {
        k.Name = "Changed Again!";
        k = null;
    }
}
0 голосов
/ 26 мая 2010

Utilityclass.TestMethod не может изменить локальную переменную p, чтобы она указывала на другой объект Person, поскольку вы не передаете по ссылке, но он по-прежнему может свободно вызывать любые методы или изменять любые свойства передаваемого объекта , Таким образом, свойство Name может быть изменено в пределах Utilityclass.TestMethod.

0 голосов
/ 26 мая 2010

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

Таким образом, результат меняется.

0 голосов
/ 26 мая 2010

Когда вы передаете P методу тестирования, вы передаете его местоположение в памяти, а не копию объекта. Ссылка выбирается в теле метода, а исходное значение изменяется.

...