2013-02-28 71 views
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。 这是为什么,我该怎么办?当然,我希望[这个]有一个对新克隆对象的引用。

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

这是因为您没有附加新创建的对象的BoardPositionsOnCollectionChanged方法,而是将所述方法附加到要克隆的对象上。

这就是说,你需要指定参考明确地使用,或编译器将假定this,这是原来的对象:

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

// working code: 
gameBoard.BoardPositions.CollectionChanged += gameBoard.BoardPositionsOnCollectionChanged; 
+1

明确指出事件处理程序的好处!我没有想到这一点。 – 2013-02-28 08:04:21

+0

为了解决这个问题,我ban my了几个小时。非常感谢! – lightxx 2013-02-28 08:07:08

0

为什么事件点到老对象的目标?

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

您可以尝试在不同的“初始化”方法融通assignement,并调用它的新实例:

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


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

应该努力!

0

您的问题是这一行:

gameBoard.BoardPositions.CollectionChanged += BoardPositionsOnCollectionChanged;

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

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