Equals方法在Debug和Release模式下表现不同。

3

我有一个大项目,第一次在发布模式下进行测试时发现了一个大问题。这段代码查找所有当前可见列表中但不在数据库中的对象,并将它们添加到另一个列表以备后续删除。通常情况下,如果没有差异,toRemove 保持为空。但在 发布 模式下,当没有差异时,整个 visibleList 都会被添加到 toRemove 中。

// Find which elements are in the visible list that do not exist in the database
foreach(var tr in visibleList.Where((entry) =>
   {
       return fullList.Contains(entry);
   }))
{
   toRemove.Add(tr);
}

经过分析代码并运行一些测试,我将问题缩小到了这个方面:

// Returns true in DEBUG mode, but false in RELEASE mode
//  (when entry does in fact equal fullList[0])
bool equalityResult = entry.Equals(fullList[0]);

fullListtoRemove只是基本的C# List<Entry>对象,而visibleList是一个ObservableCollection<Entry>

Entry.Equals没有被重载。

为什么这个函数在两种配置之间会有不同的行为?我该怎么做才能修复这个问题?

编辑:大部分Entry的定义如下:

public class Entry : INotifyPropertyChanged
{
    public String Name { get; set; }
    public String Machine { get; set; }
    public Int32 Value { get; set; }

    // Output values separated by a tab.
    public override string ToString()
    {
        return String.Format("{0}\t{1}\t{2}", Name, Machine, Value);
    }

    public String ToCSVString()
    {
        return String.Format("{0},{1},{2}", Name, Machine, Value);
    }

    #region WPF Functionality
    // Enable one-way binding in WPF
    public event PropertyChangedEventHandler PropertyChanged;
    protected void NotifyPropertyChanged(string name)
    {
        PropertyChangedEventHandler h = PropertyChanged;
        if (h != null)
        {
            h(this, new PropertyChangedEventArgs(name));
        }
    }
    #endregion

    // ...
}

编辑: 我实现了Entry.Equals,这解决了问题。事实证明,顶部的某些链接错误导致我的代码中的Entry.Equals更改被排除在发布构建之外。解决了这个问题并实现了Equals后,一切都像魔法般正常工作。然而,我需要重写这个方法让我感到有点难过,似乎要做太多的工作。


Entry是一个类。它没有什么非常奇特的,只是保存我从数据库中读取的数据。大约有六个字符串值和一些用于格式化它们的函数。 - Sessamekesh
2
发布 Entry 的声明 - Brandon
1
用ReferenceEquals替换Equals,看看差异是否消失。它不应该消失。然后,找出为什么有时所有的“entries”都是对同一个对象的引用。换句话说,查看生成列表的代码。 - usr
你写了一个 Equals 的覆盖方法吗?它在哪里? - jamesSampica
我尝试重写Equals来检查我的成员,但它没有起作用。我尝试用ReferenceEquals替换Equals,但在调试和发布模式下都会返回false(这是一个开始,但我希望在两种情况下都返回“true”)。 - Sessamekesh
请在MSDN中查看详细的信息和样本站点Object.Equals Method (Object) (System) - khan
2个回答

0
如果您的Entry类中没有定义Equals实现,那么默认情况下(假设它是一个类而不是结构体),Equals只会执行引用比较。请参见Equals方法的默认行为是什么? 例如:
public class AgeWrapper {
    public int Age { get; set; }
    public AgeWrapper( int age ) { this.Age = age; }
}

public void DoWork() {
   AgeWrapper a = new AgeWrapper(21);
   AgeWrapper b = new AgeWrapper(21);
   AgeWrapper c = a;

   Console.WriteLine( a.Equals(b) ); // prints false;

   Console.WriteLine( a.Equals(c) ); // prints true;
}

唯一让它按照你的期望工作的方法,就是提供自己的Equals比较。

既然你要这样做,你需要重写GetHashCode,使得两个值生成一致的结果。神奇的Jon Skeet可以帮助你正确地完成这个任务。

不应该使用ReferenceEquals - 你关心的是对象中包含的值,而不是它们存储在内存中的位置。

public class Entry : INotifyPropertyChanged
{
    public String Name { get; set; }
    public String Machine { get; set; }
    public Int32 Value { get; set; }

    public override bool Equals( object other ) 
    {
        Entry otherEntry = other as Entry;

        if ( otherEntry == null ) { return false; }

        return 
            otherEntry.Name.Equals( this.Name ) &&
            otherEntry.Machine.Equals( this.Machine ) &&
            otherEntry.Value.Equals( this.Value );
     }


     public override int GetHashCode()
     {
          // Thanks Jon Skeet!
          unchecked // Overflow is fine, just wrap
          {
              int hash = (int) 2166136261;

              hash = hash * 16777619 ^ this.Name.GetHashCode();
              hash = hash * 16777619 ^ this.Machine.GetHashCode();
              hash = hash * 16777619 ^ this.Value.GetHashCode();

              return hash;
          }
     }
}

以上假设NameMachineValue定义了您的对象的标识

1
我想到了这一点,但在“调试”中它的行为与“发布”中不同。在“发布”中,Equals是按引用传递的,但在“调试”中是按值传递的吗?我还尝试过重载Equals,但没有帮助。 - Sessamekesh
@user2589028 - 你在调试模式下可能是幸运的,而且你用于比较的对象引用是相同的引用。在发布模式下,其他因素会导致你与克隆对象进行比较。 - antiduh
我会再试一次。谢谢! - Sessamekesh

0

Equals方法,除非被覆盖,否则对于类进行引用比较,无论是在调试还是发布版本中。

所以,你的问题在于列表包含了对象的副本(克隆),可能是通过你喜欢的ORM回传到数据库后。如果你在调试器中检查它们的属性,它们看起来是相同的,但就Equals而言,这些是不同的实例。

很难说你的构建之间有什么区别(你的ORM是否配置不同?),但事实上你可能想要创建自己的Equals覆盖(或自定义IEqualityComparer实现)以确保你可以比较由ORM实例化的对象。


我尝试再次重载Equals运算符,依次比较三个元素 - 但没有成功。 - Sessamekesh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接