简体   繁体   English

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

[英]Comparing two arrays in a generic .Equals method

Please see the code below: 请参见下面的代码:

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

Say value1 and value2 are arrays of strings. 假设value1和value2是字符串数组。 The code above returns false even if the arrays have the same contents. 即使数组具有相同的内容,上面的代码也会返回false。 How can I ensure that true is returned if they contain the same contents? 如果它们包含相同的内容,如何确保返回true?

I have looked here: How to compare arrays in C#? 我在这里看过: 如何在C#中比较数组? and tried this: 并尝试了这个:

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

The compiler error is: SequenceEqual cannot be inferred from usage. 编译器错误是:无法从使用情况推断出SequenceEqual。

Part of the problem is that field.GetValue(other) returns an object . 问题的一部分是field.GetValue(other)返回一个object You'll likely find that using recursive reflection to compare untyped objects will frustrate you. 您可能会发现,使用递归反射来比较未类型化的对象会让您感到沮丧。

In order to make this work you'd have to determine whether the field is a collection, and if it is, compare the contents of the collection instead. 为了使此工作有效,您必须确定该字段是否为集合,如果是,则比较该集合的内容。 Then what if one of collections contains a collection? 那么如果其中一个集合包含一个集合怎么办? This is doable, but it's a headache. 这是可行的,但令人头疼。

Whatever this object is that you're trying to compare, it's much simpler if you implement IEquatable<T> or define an IEqualityComparer<T> for the types you want to compare. 无论您要尝试比较的对象是什么,如果实现IEquatable<T>或为要比较的类型定义IEqualityComparer<T> ,它都会更加简单。 Your Equals method then checks equality explicitly for the various members. 然后,您的Equals方法显式检查各个成员的相等性。 If those members are reference types, you define equality for those as well. 如果这些成员是引用类型,则也要为它们定义相等性。 Then, if those types have more nested properties you don't have drill down into all of those nested properties and compare their properties. 然后,如果这些类型具有更多的嵌套属性,则无需深入研究所有这些嵌套属性并比较它们的属性。

What this ultimately requires is that your method "knows" what types it's comparing so that it knows exactly how to compare them. 这最终需要的是您的方法“知道”正在比较的类型,以便确切知道如何比较它们。 If you're using reflection to figure out what the properties are and then trying to check them for equality, it's possible, but it's difficult and potentially broken by future changes. 如果您使用反射来找出属性是什么,然后尝试检查它们是否相等,则可以,但是很困难,并且可能会被将来的更改破坏。

Here's an example using a few classes with nested properties. 这是一个使用一些带有嵌套属性的类的示例。

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

Each class with a nested collection property uses SequenceEqual to determine whether the collections match. 每个具有嵌套集合属性的类都使用SequenceEqual来确定集合是否匹配。 But how does it figure out whether individual items in those collections are equal to each other? 但是,如何确定这些集合中的各个项目是否彼此相等? It doesn't. 没有。 It assumes that those types will be able to to determine equality. 它假定这些类型将能够确定相等性。

Now if the conditions that make two instances of a class equatable change, the details can be isolated to that class. 现在,如果使一个类的两个实例相等的条件发生了变化,则可以将详细信息隔离到该类。

Suppose I can't modify one of these classes to support IEquatable<T> , or for some reason it doesn't make sense to. 假设我无法修改其中一个类来支持IEquatable<T> ,或者由于某种原因,这样做是没有意义的。 Perhaps equality is determined differently in different contexts. 也许在不同情况下平等是不同的。 Maybe a class has an ID property and in some context all I care about is that if two objects have the same ID , they're equal. 也许一个类具有ID属性,并且在某些情况下,我关心的是如果两个对象具有相同的ID ,则它们相等。

In that case I could move that equality comparison into its own class and only use it when I want to. 在那种情况下,我可以将该相等比较移到其自己的类中,并且仅在需要时使用它。 It's no longer "built in" to the class. 它不再“内置”在班级中。

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

Now when I use SequenceEqual on two collections of FooBar I would do this: 现在,当我在FooBar两个集合上使用SequenceEqual时,我将执行以下操作:

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

Another benefit of this is that you can re-use equality comparisons. 这样做的另一个好处是,您可以重复使用相等比较。 Suppose you have two different classes with a list of Bar . 假设您有两个带有Bar列表的不同类。 Now both classes don't have to inspect Bar and its nested properties to determine equality, which would duplicate code. 现在,这两个类都不必检查Bar及其嵌套属性来确定相等性,这将重复代码。 That equality check is located elsewhere and can be shared by both when that's desirable. 该相等性检查位于其他位置,并且可以在需要时由两者共享。

The issue most likely has to do with referential equals vs value equality. 这个问题很可能与引用相等vs价值相等有关。 If you compare 2 objects that don't override Equals you are comparing that they are the same item not the same value. 如果比较两个不覆盖等于的对象,则表示它们是同一项目,但值不同。

The SequenceEqual error you are seeing has to do with the compiler not being sure of the type you are using to compare. 您看到的SequenceEqual错误与编译器有关,无法确定您要比较的类型。
https://msdn.microsoft.com/en-us/library/bb348567(v=vs.110).aspx https://msdn.microsoft.com/en-us/library/bb348567(v=vs.110).aspx

You can specify the type you want to use to check equality 您可以指定要用于检查相等性的类型

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM