如何更新按值传递的引用类型的引用

时间:2019-05-09 10:48:00

标签: c# pass-by-reference variable-assignment pass-by-value ref

我正在使用一个按值在类之间传递对象的框架,我需要更新对其中一个对象的引用。我可以很好地更改原始对象的属性值,但似乎无法弄清楚如何更改对全新对象的引用。在后面的一个类中,我正在从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 个答案:

答案 0 :(得分:1)

您在内部局部函数中使用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;
}