【问题标题】:Equals method behaves differently in Debug and Release modesEquals 方法在 Debug 和 Release 模式下的行为不同
【发布时间】:2014-07-17 21:20:17
【问题描述】:

我有一个大项目,我刚刚开始在发布模式下进行第一次测试,但我发现了一个大问题。此代码查找当前可见列表中但不在数据库中的所有对象,并将它们添加到另一个列表以供以后删除。通常,如果没有差异,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,一切都像魅力一样。不过,我不得不重写该方法让我感到难过,这似乎有点太多工作了。

【问题讨论】:

  • Entry 是一个类。它没有任何非常奇特的东西,它只是保存我从数据库中读取的数据。六个左右的字符串值,以及几个格式化它们的函数。
  • 张贴Entry的声明
  • 用 ReferenceEquals 替换 Equals,看看差异是否消失。它不应该。然后,找出为什么有时所有“条目”都是对同一个对象的引用。换句话说,看看生成列表的代码。
  • 您是否为Equals 编写了覆盖?那是哪里?
  • 我尝试覆盖Equals 来检查我的成员,但没有成功。我尝试用ReferenceEquals 替换Equals,它会在调试和发布中返回false(这是一个开始,但我在这两种情况下都在寻找一个'true'。

标签: c# equals


【解决方案1】:

如果你没有在你的Entry 类中定义一个Equals 实现,那么,假设它是一个类而不是一个结构,Equals 默认只执行一个引用比较。见What is the default behavior of 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 以便两者生成一致的值。神奇的 Jon Skeet 可以通过 the proper way 帮助您做到这一点。

想要 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 定义了您的对象的身份

【讨论】:

  • 看,我想到了,但它在 Debug 中的行为有所不同,但在 Release 中却没有。 EqualsRelease 中是通过引用,但在 Debug 中是通过值?我也尝试过重载Equals,但没有帮助。
  • @user2589028 - 您在调试模式下一定很幸运,并且您正在执行比较的对象引用是相同的引用。发布模式中的其他内容导致您与克隆对象进行比较。
  • 我会再试一次。谢谢!
【解决方案2】:

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

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

很难说您的构建之间的区别在哪里(您的 ORM 配置是否不同?),但实际上您可能想要create your own Equals override(或自定义IEqualityComparer 实现)以确保您可以比较对象由 ORM 实例化。

【讨论】:

  • 我尝试再次重载 Equals 运算符,依次比较三个元素中的每一个 - 没有成功。
猜你喜欢
  • 1970-01-01
  • 2023-03-31
  • 1970-01-01
  • 2014-11-01
  • 1970-01-01
  • 2015-06-14
  • 2022-06-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多