比较通用.Equals方法中的两个数组

时间:2017-12-13 15:14:22

标签: c# .net

请参阅以下代码:

 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?

我看过这里:How to compare arrays in C#?并尝试了这个:

bool isEqual = Enumerable.SequenceEqual(value1, value2);

编译器错误是:无法从使用中推断出SequenceEqual。

2 个答案:

答案 0 :(得分:1)

部分问题是field.GetValue(other)会返回object。您可能会发现使用递归反射来比较无类型对象会让您感到沮丧。

为了完成这项工作,您必须确定该字段是否为集合,如果是,则比较集合的内容。那么如果其中一个集合包含集合呢?这是可行的,但它很头疼。

无论您正在尝试比较这个对象,如果您为要比较的类型实施IEquatable<T>或定义IEqualityComparer<T>,情况就会简单得多。然后,您的Equals方法会明确检查各个成员的相等性。如果这些成员是引用类型,那么您也可以为这些成员定义相等性。然后,如果这些类型具有更多嵌套属性,则不必深入查看所有嵌套属性并比较它们的属性。

这最终需要的是你的方法&#34;知道&#34;它的类型是什么类型,以便它确切地知道如何比较它们。如果您使用反射来确定属性是什么,然后尝试检查它们是否相等,那么它是可能的,但它很难并且可能会被未来的更改所打破。

这是一个使用嵌套属性的几个类的例子。

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,它们就相等。

在这种情况下,我可以将相等比较移动到它自己的类中,并且只在我想要的时候使用它。它不再是&#34;内置&#34;上课。

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();
    }
}

现在当我在SequenceEqual的两个集合上使用FooBar时,我会这样做:

FooBars.SequenceEqual(other.FooBars, new FooBarComparer())

这样做的另一个好处是可以重复使用相等比较。假设您有两个不同的类,其列表为Bar。现在,这两个类都不必检查Bar及其嵌套属性以确定相等性,这将重复代码。该等级检查位于其他地方,并且可以在两者共享时进行共享。

答案 1 :(得分:0)

这个问题很可能与参考等于与价值平等有关。如果比较2个不重写等于的对象,则表示它们是相同的项目,而不是相同的值。

您看到的SequenceEqual错误与编译器无法确定您用于比较的类型有关。
https://msdn.microsoft.com/en-us/library/bb348567(v=vs.110).aspx

您可以指定要用于检查相等性的类型

public List<MyUser> getList(String stringJson){ List<MyUser> myUser = null; Gson gson = new Gson(); Type type = new TypeToken<List<MyUser>>() {}.getType(); myUser = gson.fromJson(stringJson, type); return myUser; }