0

首先,很抱歉无法提出更好的标题。不过我试过了。

我有这样的课

public class GameBoard : ICloneable {       
    private bool _isCloned;

    public ObservableCollection<BoardPosition> BoardPositions { get; set; }

    public GameBoard() {
        BoardPositions = new ObservableCollection<BoardPosition>();
        //this.BoardPositions.CollectionChanged += BoardPositionsOnCollectionChanged;
    }

    private void BoardPositionsOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs) {
        // if this event is raised from the NEW object, [this] still points to the OLD object
    }

    public object Clone() {
        //var gameBoard = (GameBoard)this.MemberwiseClone();
        var gameBoard = new GameBoard {
            // make it VERY clear this is just a clone
            _isCloned = true,
            // deep copy the list of BoardPositions
            BoardPositions =
                new ObservableCollection<BoardPosition>(this.BoardPositions.Select(p => p.Clone()).Cast<BoardPosition>())
        };

        gameBoard.BoardPositions.CollectionChanged += BoardPositionsOnCollectionChanged;
        // why does the target of the event point to the old object?!?
        return gameBoard;
    }
}
public class BoardPosition : ICloneable {
    public int[] Coordinates { get; set; }

    public BoardPosition(int[] coordinates) {
        Coordinates = coordinates;
    }

    public object Clone() {
        return new BoardPosition(new int[]{this.Coordinates[0], this.Coordinates[1], this.Coordinates[2]});
    }
}

它实现了 ICloneable。在 Clone 方法中,我深度复制 ObservableCollection,并附加 CollectionChanged 事件处理程序。

现在的问题是,当新的克隆对象的 CollectionChanged 事件触发时,[this] 指向被克隆的旧对象。这可以很容易地观察到,因为在引发事件时 _isCLoned 始终为 false,即使在克隆期间它被设置为 true。为什么会这样,我该怎么办?我当然希望 [this] 引用新的克隆对象。

var gameBoard = new GameBoard();
        gameBoard.BoardPositions.Add(new BoardPosition(new[] {1, 2, 3}));

        var clonedBoard = (GameBoard)gameBoard.Clone();
        clonedBoard.BoardPositions.Add(new BoardPosition(new[] { 2, 3, 4 }));
4

3 回答 3

4

那是因为您没有附加BoardPositionsOnCollectionChanged新创建对象的方法,而是将方法附加到正在克隆的对象上。

也就是说,您需要明确指定要使用的引用,否则编译器将假定this,这是原始对象:

// your code ("this." is added implicitly):
gameBoard.BoardPositions.CollectionChanged += this.BoardPositionsOnCollectionChanged;

// working code:
gameBoard.BoardPositions.CollectionChanged += gameBoard.BoardPositionsOnCollectionChanged;
于 2013-02-28T08:02:12.390 回答
0

为什么事件的目标指向旧对象?

因为您在旧对象 Clone 方法中分配处理程序。我在这里做一些猜测(我这里没有 C# 编译器),但我记得我过去遇到过类似的问题。在克隆中,“this”是旧对象。BoardPositionsOnCollectionChanged 在该点解析为当前的“this”引用,因此它将始终处理对“old”对象的引用。

您可以尝试将分配分解为不同的“Init”方法,并在新实例上调用它:

void Init() {
   BoardPositions.CollectionChanged += BoardPositionsOnCollectionChanged;
}


public object Clone() {
    ...
    gameBoard.Init()
    return gameBoard;
}

那应该工作!

于 2013-02-28T08:00:59.993 回答
0

你的问题是这一行:

gameBoard.BoardPositions.CollectionChanged += BoardPositionsOnCollectionChanged;

BoardPositionsOnCollectionChanged是被克隆对象的成员,所以当调用时,'this' 将是旧对象。

而是在 ctor 中设置您的事件处理程序。

于 2013-02-28T08:02:14.997 回答