我有一个很大的项目,我刚刚在发布模式下第一次接受测试,而且我发现了一个大问题。此代码查找当前可见列表中但不在数据库中的所有对象,并将它们添加到另一个列表中以便以后删除。通常,如果没有差异,toRemove
仍为空。但是在发布模式下,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]);
fullList
和toRemove
只是基本的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
之后,一切都像魅力一样。让我感到难过的是,我必须覆盖这种方法,看起来有点太多了。
答案 0 :(得分:0)
如果您尚未在Equals
类中定义Entry
实现,那么,假设它是一个类而不是结构,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;
}
}
}
以上假设Name
,Machine
和Value
定义了对象的身份。
答案 1 :(得分:0)
Equals
方法,除非重写,否则对类进行引用比较,无论是在调试版本还是发布版本中。
所以,你的问题是该列表包含对象的副本(克隆),可能是在通过您最喜欢的ORM往返数据库之后。如果在调试器中检查它们的属性,它们看起来是相同的,但就Equals
而言,这些是不同的实例。
很难说你的构建之间的区别在哪里(你的ORM配置不同吗?),但实际上你可能希望create your own Equals
override(或自定义IEqualityComparer
实现)来确保您可以比较ORM实例化的对象。