[英]Comparing two arrays in a generic .Equals method
請參見下面的代碼:
public virtual bool Equals(T other)
{
if (other == null)
return false;
Type t = GetType();
Type otherType = other.GetType();
if (t != otherType)
return false;
FieldInfo[] fields = t.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
foreach (FieldInfo field in fields)
{
object value1 = field.GetValue(other);
object value2 = field.GetValue(this);
if (value1 == null)
{
if (value2 != null)
return false;
}
else if (!value1.Equals(value2))
return false;
}
return true;
}
假設value1和value2是字符串數組。 即使數組具有相同的內容,上面的代碼也會返回false。 如果它們包含相同的內容,如何確保返回true?
我在這里看過: 如何在C#中比較數組? 並嘗試了這個:
bool isEqual = Enumerable.SequenceEqual(value1, value2);
編譯器錯誤是:無法從使用情況推斷出SequenceEqual。
問題的一部分是field.GetValue(other)
返回一個object
。 您可能會發現,使用遞歸反射來比較未類型化的對象會讓您感到沮喪。
為了使此工作有效,您必須確定該字段是否為集合,如果是,則比較該集合的內容。 那么如果其中一個集合包含一個集合怎么辦? 這是可行的,但令人頭疼。
無論您要嘗試比較的對象是什么,如果實現IEquatable<T>
或為要比較的類型定義IEqualityComparer<T>
,它都會更加簡單。 然后,您的Equals
方法顯式檢查各個成員的相等性。 如果這些成員是引用類型,則也要為它們定義相等性。 然后,如果這些類型具有更多的嵌套屬性,則無需深入研究所有這些嵌套屬性並比較它們的屬性。
這最終需要的是您的方法“知道”正在比較的類型,以便確切知道如何比較它們。 如果您使用反射來找出屬性是什么,然后嘗試檢查它們是否相等,則可以,但是很困難,並且可能會被將來的更改破壞。
這是一個使用一些帶有嵌套屬性的類的示例。
public class Foo : IEquatable<Foo>
{
public int FooValue { get; set; }
public List<Bar> Bars { get; set; }
public bool Equals(Foo other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
if (FooValue != other.FooValue) return false;
if (Bars == null ^ other.Bars == null) return false;
return Bars == null || Bars.SequenceEqual(other.Bars);
}
}
public class Bar : IEquatable<Bar>
{
public int BarValue { get; set; }
public List<FooBar> FooBars { get; set; }
public bool Equals(Bar other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
if( BarValue != other.BarValue) return false;
if (FooBars == null ^ other.FooBars == null) return false;
return FooBars==null || FooBars.SequenceEqual(other.FooBars);
}
}
public class FooBar : IEquatable<FooBar>
{
public int FooBarValue { get; set; }
public bool Equals(FooBar other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return FooBarValue == other.FooBarValue;
}
}
每個具有嵌套集合屬性的類都使用SequenceEqual
來確定集合是否匹配。 但是,如何確定這些集合中的各個項目是否彼此相等? 沒有。 它假定這些類型將能夠確定相等性。
現在,如果使一個類的兩個實例相等的條件發生了變化,則可以將詳細信息隔離到該類。
假設我無法修改其中一個類來支持IEquatable<T>
,或者由於某種原因,這樣做是沒有意義的。 也許在不同情況下平等是不同的。 也許一個類具有ID
屬性,並且在某些情況下,我關心的是如果兩個對象具有相同的ID
,則它們相等。
在那種情況下,我可以將該相等比較移到其自己的類中,並且僅在需要時使用它。 它不再“內置”在班級中。
public class FooBar
{
public int FooBarValue { get; set; }
}
public class FooBarComparer : IEqualityComparer<FooBar>
{
public bool Equals(FooBar x, FooBar y)
{
if (ReferenceEquals(x, y)) return true;
if (x == null || y == null) return false;
return x.FooBarValue == y.FooBarValue;
}
public int GetHashCode(FooBar obj)
{
return obj.GetHashCode();
}
}
現在,當我在FooBar
兩個集合上使用SequenceEqual
時,我將執行以下操作:
FooBars.SequenceEqual(other.FooBars, new FooBarComparer())
這樣做的另一個好處是,您可以重復使用相等比較。 假設您有兩個帶有Bar
列表的不同類。 現在,這兩個類都不必檢查Bar
及其嵌套屬性來確定相等性,這將重復代碼。 該相等性檢查位於其他位置,並且可以在需要時由兩者共享。
這個問題很可能與引用相等vs價值相等有關。 如果比較兩個不覆蓋等於的對象,則表示它們是同一項目,但值不同。
您看到的SequenceEqual錯誤與編譯器有關,無法確定您要比較的類型。
https://msdn.microsoft.com/en-us/library/bb348567(v=vs.110).aspx
您可以指定要用於檢查相等性的類型
bool isEqual = Enumerable.SequenceEqual<T>(value1, value2);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.