[英]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.