Как переназначить ссылочный тип в другом классе и сделать изменения неизменными? - PullRequest
0 голосов
/ 17 сентября 2011

Вот довольно надуманный пример того, что я спрашиваю:

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;, плод в генетике больше не указывает на плод формы 1, а вместо этого на местную копию в генетике. Полагаю, я ищу способ смоделировать ключевое слово ref, где, если я переназначу генетический фрукт, фрукт Form1 также будет обновлен с изменением.

Полагаю, я мог бы создать упаковку с фруктами и вместо этого раздать ее, но это кажется немного хакерским. Также я мог бы заставить метод Engineer вызвать событие, чтобы основная форма могла переназначить значение, но необходимость делать это во многих частях моей программы также выглядит немного грязной.

Кроме того, я не могу использовать ключевое слово ref, потому что я изменяю его позже, а не в конструкторе Geneticist.

Спасибо за чтение!

1 Ответ

3 голосов
/ 17 сентября 2011

Проблема в том, что если я переназначу его, это не повлияет на оригинал в Form1.Копия сделана в Генетике, когда я печатаю фрукты = банан.

Нет, это неправильно.Копия не производится.

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

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

Так что же делать?

Факт остается фактом: вы не можете изменить1021 * параметр в целом со ссылочной семантикой (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);
...