Equals方法在调试和发布模式下的行为不同

本文关键字:模式 调试 方法 布模式 Equals | 更新日期: 2023-09-27 18:13:37

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

// 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>对象,而visibleListObservableCollection<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,一切都像一个魅力。这让我很难过,我必须重写那个方法,虽然,似乎有点太多的工作。

Equals方法在调试和发布模式下的行为不同

如果您没有在Entry类中定义Equals实现,那么,假设它是一个类而不是结构体,Equals默认情况下只执行引用比较。参见Equals Method的默认行为是什么?

例如:

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,以便两者生成一致的值。神奇的乔恩·斯基特可以帮助你用正确的方法做到这一点。

不需要 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;
          }
     }
}

上面假设Name, MachineValue定义了对象的标识

Equals方法,除非被重写,否则对类进行引用比较,无论在调试或发布版本中。

因此,您的问题是该列表包含对象的副本(克隆),可能是在通过您最喜欢的ORM往返数据库之后。如果您在调试器中检查它们的属性,它们看起来是相同的,但就Equals而言,它们是不同的实例。

很难说您的构建之间的差异在哪里(您的ORM配置不同吗?),但实际上您可能想要创建自己的Equals覆盖(或自定义IEqualityComparer实现),以确保您可以比较由ORM实例化的对象。