[英]Weird behavior of event handlers when deep cloning an object
首先,很抱歉无法提出更好的标题。 我试过了。
我有这样的课
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]指向被克隆的OLD对象。 这很容易观察到,因为引发事件时_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 }));
那是因为您没有附加新创建对象的BoardPositionsOnCollectionChanged
方法,而是在要克隆的对象上附加了所述方法。
也就是说,您需要指定要显式使用的引用,否则编译器将假定this
,它是原始对象:
// your code ("this." is added implicitly):
gameBoard.BoardPositions.CollectionChanged += this.BoardPositionsOnCollectionChanged;
// working code:
gameBoard.BoardPositions.CollectionChanged += gameBoard.BoardPositionsOnCollectionChanged;
为什么事件的目标指向旧对象?
因为您是在旧对象的Clone方法内分配处理程序。 我在这里做一些猜测(这里没有C#编译器),但是我记得我过去遇到过类似的问题。 在克隆中,“ this”是旧对象。 BoardPositionsOnCollectionChanged会在此时解析为当前的“ this”引用,因此它将始终处理对“ old”对象的引用。
您可以尝试使用其他“ Init”方法将分配因素分解,然后在新实例上调用它:
void Init() {
BoardPositions.CollectionChanged += BoardPositionsOnCollectionChanged;
}
public object Clone() {
...
gameBoard.Init()
return gameBoard;
}
那应该工作!
您的问题是这一行:
gameBoard.BoardPositions.CollectionChanged += BoardPositionsOnCollectionChanged;
BoardPositionsOnCollectionChanged
是要克隆的对象的成员,因此当被调用时,“ this”将成为旧对象。
而是在ctor中设置事件处理程序。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.