【问题标题】:How to re-assign a reference type in another class and have changes stick to original?如何在另一个类中重新分配引用类型并使更改保持原样?
【发布时间】:2025-12-13 20:15:01
【问题描述】:

这是我所问的一个相当人为的例子:

public partial class Form1 : Form
{
    private Fruit fruit;

    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        fruit = new Apple();

        Geneticist geneticist = new Geneticist(fruit);

        // Later on in program..

        geneticist.Engineer();

        Console.WriteLine(fruit.Color);

        // Still red because a copy of fruit was made in Geneticist class.
    }
}

class Fruit
{
    public string Color { get; set; }
}

class Apple : Fruit
{
    public Apple()
    {
        Color = "Red";
    }
}

class Banana : Fruit
{
    public Banana()
    {
        Color = "Yellow";
    }
}

class Geneticist
{
    private Fruit fruit;
    private Banana banana;

    public Geneticist(Fruit fruit)
    {
        this.fruit = fruit;
        this.banana = new Banana();
    }

    public void Engineer()
    {
        fruit = banana;
    }
}

基本上,我在主窗体中存储了一个水果作为成员变量。我希望能够将它传递给我的遗传学家类,然后让它重新分配值。

当我键入fruit = banana; 时,遗传学家中的水果不再指向 Form1 水果,而是指向遗传学家中的本地副本。我想我正在寻找一种模拟 ref 关键字的方法,如果我重新分配遗传学家水果,Form1 水果也会随着变化而更新。

我想我可以创建一个水果包装并将其传递给周围,但这似乎有点不合时宜。我也可以让Engineer 方法引发一个事件,以便主窗体可以重新分配值,但在我的程序的许多部分都必须这样做似乎也有点混乱。

另外,我不能使用 ref 关键字,因为我稍后会修改它,而不是在 Geneticist 的构造函数中。

感谢阅读!

【问题讨论】:

  • 如果您不想将 Form1 和遗传学家紧密结合起来,那么这个事件听起来是个好主意。

标签: c# reference ref


【解决方案1】:

这样做的问题是,如果我重新分配它,它不会影响 Form1 中的原始文件。当我输入水果时,遗传学家会复制一份= 香蕉。

不,这是错误的。没有复制。

发生的情况是,您用对 Banana 的引用覆盖了您手头的 Apple 引用。就好像给你一个苹果放在口袋里,拿着它一会儿,你把它放在地上,拿起一根香蕉,放进口袋里。

稍后你决定吃原苹果时,原苹果会完好无损,但不是因为你复制了。只是因为你只是对它失去了兴趣,而是得到了一种完全不相关的水果。

那该怎么办?

事实仍然是,您不能使用引用语义将Fruit 参数作为一个整体修改(正如您自己所说,CLR 不允许将引用存储为类成员)。

如果您希望Geneticist 修改Fruit 引用,那么您必须围绕它创建一个包装器。但最实用的解决方案是让调用代码配合Geneticist

class Geneticist
{
    private Fruit fruit;
    private Banana banana;

    public Geneticist(Fruit fruit)
    {
        this.fruit = fruit;
        this.banana = new Banana();
    }

    public Fruit Engineer()
    {
        fruit = banana;
        return fruit; // return the new value
    }
}

以及调用代码:

fruit = new Apple();

Geneticist geneticist = new Geneticist(fruit);
fruit = geneticist.Engineer(); // use the return value this way
Console.WriteLine(fruit.Color);

另一种可行的方法

Geneticist 知道如何自己修改水果怎么样?

class Geneticist
{
    private Fruit fruit;

    private readonly Banana banana;

    private readonly Action<Fruit> engineer;

    public Geneticist(Fruit fruit, Action<Fruit> engineer)
    {
        this.fruit = fruit;
        this.banana = new Banana();
        this.engineer = engineer;
    }

    public void Engineer()
    {
        this.engineer(this.banana);
    }
}

以及调用代码:

Fruit fruit = new Apple();
Geneticist geneticist = new Geneticist(fruit, f => { fruit = f; });
geneticist.Engineer();
Console.WriteLine(fruit.Color);

【讨论】:

  • 呃,是的,我知道。我为没有正确措辞而道歉。我会在原帖中修复它。
  • @KylePrice:编辑完成。也许一种不那么纯粹但更实用的方法是可以接受的?
  • Jon:这就是我现在在部分程序中使用的方法。但是,问题是有时我无法返回fruit,因为函数可能不会在那里结束或者调用者不是主窗体。
  • @KylePrice:用另一种方法更新,检查一下。本质上,这是包装器方法,只有编译器会帮助您生成包装器,而不会给您带来麻烦。
  • 嗯,这似乎可行!感谢您的所有帮助。