【问题标题】:C# Collection<T>.Remove(T item) not working though object data is equal尽管对象数据相等,但 C# Collection<T>.Remove(T item) 不起作用
【发布时间】:2017-01-04 23:04:18
【问题描述】:

我有两个集合,一个用于 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 上上传文件。

最好的问候

【问题讨论】:

  • 用一个值引用它(与你删除的书相同)并在循环中使用它

标签: c# wpf mvvm collections


【解决方案1】:

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

如果类型 T 实现了 IEquatable 泛型接口,则等式 comparer 是该接口的 Equals 方法;否则, 默认相等比较器是 Object.Equals。

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

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

您在上面所做的是创建一堆BookViewModel (s),然后尝试将它们从您的收藏中删除。但是您刚刚创建了 book,因此它以前从未存在于您的收藏中。

如果您希望能够做到这一点,那么至少您应该在您的类型上实现IEquatable&lt;T&gt; 接口,以便Remove 方法可以使用它来确定您要删除的项目是否在集合,但不仅使用引用相等,还​​使用其他相等。

这是一个example(我复制了下面的代码),它通过检查两个部分是否具有相同的 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&lt;T&gt;,所以这里有区别:

IEquatable&lt;T&gt; 测试两个对象是否相等。这就是你需要的。

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

【讨论】:

  • 感谢您的回答。那讲得通。那我是不是必须自己实现比较 Equals() 方法逻辑?
  • 至少,你应该这样做。
  • 是的。所以谢谢这解决了我的问题。奇迹般有效。谢谢你的详细回答。
【解决方案2】:

与已经说过的内容一致。您可以在 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 类中具有哪些属性以及您可能需要进行比较。

【讨论】:

    【解决方案3】:

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

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

    我认为你正在为此苦苦挣扎。您有具有相同属性但引用不同的对象。

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

    如果您决定实现您的比较器,您可以关注这篇MSDN 文章。基本上你需要实现IComparer&lt;T&gt;

    如果您使用 List&lt;Book&gt;,您可以关注 MSDN 并在您的 Book 上实现 IEquatable&lt;Book&gt;

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-10-25
      • 1970-01-01
      • 2018-06-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多