Как обновить ссылку на тип ссылки, переданную по значению - PullRequest
0 голосов
/ 09 мая 2019

Я работаю со структурой, которая передает объекты между классами по значению, и мне нужно обновить ссылку на один из этих объектов. Я могу просто изменить значения свойств исходного объекта, но не могу понять, как изменить ссылку на совершенно новый объект. В одном из последних классов я получаю довольно сложный объект из API, поэтому я хочу просто обновить ссылку, а не пытаться выполнить глубокую копию.

Я попытался вызвать SwapPerson{One,Two,Three,Four} в моем примере кода без намека на успех. Выход всегда:

Main.person: Groucho Marx is 128 years old!
Main.person: Groucho Marx is 129 years old!
Main.person: Groucho Marx is 129 years old!

Я надеюсь, что есть простое решение, которое я упускаю из-за позднего часа, так что любой вклад очень ценится.

    public class Person
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public int Age { get; set; }
        public override string ToString()
        {
            return $"{FirstName} {LastName} is {Age} years old!";
        }
    }
    public class Foo
    {
        private Person person;

        public Foo(Person person)
        {
            this.person = person;
        }

        public void SetAge(int age)
        {
            person.Age = age;
        }

        public void SwapPersonOne(Person newPerson)
        {
            person = newPerson;
        }

        public void SwapPersonTwo(ref Person newPerson)
        {
            person = newPerson;
        }

        public void SwapPersonThree(Person newPerson)
        {
            LocalSwap(ref person);

            void LocalSwap(ref Person oldPerson)
            {
                oldPerson = newPerson;
            }
        }

        public void SwapPersonFour(Person newPerson)
        {
            LocalSwap(ref person, ref newPerson);

            void LocalSwap(ref Person oldPerson, ref Person _newPerson)
            {
                oldPerson = _newPerson;
            }
        }
    }
    static void Main(string[] args)
    {
        Person person = new Person { FirstName = "Groucho", LastName = "Marx", Age = 128 };

        Console.WriteLine($"{nameof(Main)}.{nameof(person)}: {person}");

        var foo = new Foo(person);

        foo.SetAge(129);

        Console.WriteLine($"{nameof(Main)}.{nameof(person)}: {person}");

        var charlie = new Person { FirstName = "Charlie", LastName = "Chaplin", Age = 130 };

        //foo.SwapPersonOne(charlie);
        //foo.SwapPersonTwo(ref charlie);
        //foo.SwapPersonThree(charlie);
        foo.SwapPersonFour(charlie);

        Console.WriteLine($"{nameof(Main)}.{nameof(person)}: {person}");

        Console.ReadLine();
    }

1 Ответ

1 голос
/ 09 мая 2019

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

    public void SwapPersonFour(Person newPerson)
    {
        LocalSwap(ref person, ref newPerson);

        void LocalSwap(ref Person oldPerson, ref Person _newPerson)
        {
            oldPerson = _newPerson;
        }
    }

oldPerson и _newPerson передаются локальной функции по ссылке, но newPerson передается SwapPersonFour по значению.

Кроме того, обновляется только oldPerson, поэтому теперь и oldPerson, и _newPerson относятся к одному и тому же Person.

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

public void SwapPersonFour(ref Person newPerson)

В комментарии упоминается, что это не сработало, поэтому я собрал поспешный юнит-тест, чтобы проверить, что я что-то упустил. (Я все время скучаю, поэтому пишу юнит-тесты.)

[TestClass]
public class UnitTest1
{
    private Person _person;

    [TestMethod]
    public void TestSwappingPerson()
    {
        _person = new Person { FirstName = "Scott" };
        var newPerson = new Person() { FirstName = "Bob" };
        SwapPersonFour(ref newPerson);
        Assert.AreEqual("Bob", _person.FirstName);
    }

    public void SwapPersonFour(ref Person newPerson)
    {
        LocalSwap(ref _person, ref newPerson);

        void LocalSwap(ref Person oldPerson, ref Person localNewPerson)
        {
            oldPerson = localNewPerson;
        }
    }
}

SwapPersonFour заменяет поле _person ссылкой на newPerson. Это на самом деле ничего не меняет, потому что пока он обновляет _person, он не обновляет newPerson. Когда это сделано, они оба ссылаются на один и тот же Person. (Вы хотели поменять их? Это может быть проблемой.)

Для чего бы это ни стоило, я бы удалил локальную функцию, потому что она на самом деле ничего не делает. Он просто берет одну вещь, которую делает метод, и вкладывает ее в дополнительную функцию. Вы можете заменить его на это и получить тот же результат - его легче читать. (На самом деле, дополнительный код мог бы упустить тот факт, что ничего не поменялось. Я не знаю, как вы, но меня совсем не смущает.)

public void SwapPersonFour(ref Person newPerson)
{
    _person = newPerson;
}

Если вы действительно хотите поменять их местами, вы можете сделать это:

public void SwapPersonFour(ref Person newPerson)
{
    var temp = _person;
    _person = newPerson;
    newPerson = temp;
}
...