C#Collection <t>。删除(T项)虽然对象数据相等但不工作

时间:2017-01-04 23:04:19

标签: c# wpf mvvm collections

我有两个集合,其中一个用于Book对象,另一个用于BookViewModel对象。 当我从BookCollection中删除Book对象时,会触发CollectionChanged事件,以便BookViewModelCollection可以删除它的BookViewModel版本的书。因此继承了与事件绑定的委托函数。

  

BookViewModelCollection.cs

private void ModelCollectionChanged(Object sender, NotifyCollectionChangedEventArgs e)
{
    if (syncDisabled == false)
    {
        syncDisabled = true;
        switch (e.Action)
        {
            case NotifyCollectionChangedAction.Add:
                foreach (var book in e.NewItems.OfType<Book>().Select(v => new BookViewModel(v)))
                {
                    this.Add(book);
                }
                break;
            case NotifyCollectionChangedAction.Remove:
                foreach (var book in e.OldItems.OfType<Book>().Select(v => new BookViewModel(v)))
                {
                    this.Remove(book);
                }
                break;
            case NotifyCollectionChangedAction.Reset:
                this.Clear();
                break;
        }
        syncDisabled = false;
    }
}

也许从Book转换为BookViewModel会出错?正在执行代码,但Remove方法返回false,并且对象仍在集合中,尽管两个对象上的数据相同。 BookViewModel包含Book类型的对象,构造函数只执行以下操作:

  

BookViewModel.cs

public BookViewModel(Book b)
{
     this.Book = b;
}

Book是一个设置Book的局部变量的属性。

注意:另一方面,当BookCollectionViewModel发生变化时更新BookCollection可以完全正确地使用相同的技术。

我希望有人能帮助我。如果您需要更多/其他信息,请告诉我。我也可以在Github上传文件。

祝你好运

3 个答案:

答案 0 :(得分:3)

remove方法在MSDN上有这个文档:

  

如果类型T实现IEquatable泛型接口,则相等   comparer是该接口的Equals方法;否则,   default equality comparer是Object.Equals。

由于您尚未实现IEquatable<T>,因此将使用默认的相等比较器。这意味着Remove方法将检查您要删除的项是否与集合中的某个对象具有相同的引用。显然不是这种情况,因为一个项目是一个对象而另一个项目是不同的对象:2个不同的引用。

foreach( var book in e.OldItems.OfType<Book>().Select( v => new BookViewModel( v ) ) ) {
   this.Remove( book );
}

您在上面所做的是创建一堆BookViewModel(s)然后尝试从您的集合中删除它们。但是你刚刚创建了book,所以它之前从未存在过。

如果您希望能够这样做,那么至少应该在类型上实现IEquatable<T>接口,以便Remove方法可以使用它来确定您想要的项目删除是在集合中,但不仅使用引用相等,而且使用其他一些相等。

这是example(我复制了下面的代码),它通过检查2个部分是否具有相同的PartId来进行相等性检查。即使对象是不同的对象(引用)。

using System;
using System.Collections.Generic;
// Simple business object. A PartId is used to identify the type of part 
// but the part name can change. 
public class Part : IEquatable<Part> {
   public string PartName { get; set; }

   public int PartId { get; set; }

   public override string ToString() {
      return "ID: " + PartId + "   Name: " + PartName;
   }
   public override bool Equals(object obj) {
      if( obj == null )
         return false;
      Part objAsPart = obj as Part;
      if( objAsPart == null )
         return false;
      else
         return Equals( objAsPart );
   }
   public override int GetHashCode() {
      return PartId;
   }
   public bool Equals(Part other) {
      if( other == null )
         return false;
      return ( this.PartId.Equals( other.PartId ) );
   }
   // Should also override == and != operators.

}

作为最后一点,你可能会遇到IComparable<T>,所以这里有不同之处:

IEquatable<T>测试两个对象是否相等。这就是你需要的。

IComparable<T>用于排序以指示在排序期间哪个对象位于另一个对象之前。例如,要对数字5,1进行排序,它会告诉您1在5之前进行升序排序。 IEquatable会告诉你1不等于5。

答案 1 :(得分:1)

  

正在执行代码,但Remove方法返回false和   尽管数据相同,但对象仍然在集合中   两个对象。

根据MSDN Remove()方法使用默认的相等比较器。默认情况下,如果对象具有相同的值属性,则它们不相同。如果它们具有相同的引用,则两个对象相同。

我认为你正在努力解决这个问题。您的对象具有相同的属性但不具有相同的引用。

要解决此问题,您可以做两件事:实现比较器或手动删除满足条件的元素。

如果您决定实施比较器,可以按照此MSDN条款进行操作。基本上你需要实现IComparer<T>

如果您使用List<Book>,则可以关注MSDN并在IEquatable<Book>上实施Book

public class Book: IEquatable<Book>
{        
    // TODO: Implement your logic.
}

答案 2 :(得分:1)

与已经说过的内容相符。您可以在this中找到具有相同图书价值的参考:

foreach (var book in e.OldItems.OfType<Book>())
{
    BookViewModel tempBVM = this.Where(x => x.Book.name == book.name &&
       c.Book.author == book.author).FirstOrDefault();
    if(tempBVM != null)
    {
        this.Remove(tempBVM);
    }
}

我只是猜测了您可能在Book类中拥有哪些属性以及您可能需要进行比较的内容。