【问题标题】:Extension method to compare all fields including IEnumerable扩展方法来比较包括 IEnumerable 在内的所有字段
【发布时间】:2018-06-29 04:44:35
【问题描述】:

我正在尝试编写一个扩展方法,根据对象的字段比较对象。

我有这个:

public static class MyExtensions 
{
    public static bool FieldsEquals(this object o, object other) 
    {
        if (ReferenceEquals(o, other))  
            return true;

        if (o == null || other == null || o.GetType() != other.GetType()) 
            return false;

        foreach (var f in o.GetType().GetFields()) 
        {
             // is this a correct test ???
             bool isEnumerable = f.FieldType != typeof(string) && 
                                 typeof(IEnumerable).IsAssignableFrom(f.FieldType);

             if (!isEnumerable) 
             {
                 if (!f.GetValue(o).Equals(f.GetValue(other))) 
                     return false;
             } 
             else 
             {
                // convert both to IEnumerable and check if equal 
             }
        }

        return true;
     }
}

我正在努力解决该领域是一个集合的情况;我需要检测这种情况,然后检查集合是否相同(相同数量的元素和f.GetValue(o)[i] == f.GetValue(other)[i]

有什么帮助吗?

【问题讨论】:

  • “我正在尝试编写通用扩展方法” - 在哪里?
  • 在我的解决方案中?
  • 你只是在使用对象和反射。没有Generics
  • @SeM 也许“generic”和“Generics”在 OP 的上下文中是不同的?
  • @UweKeim 那么他可能应该使用Generics。 :)

标签: c# c#-7.0


【解决方案1】:

好的,就像其他人已经提到的一样。有很多边缘情况..

我建议对此类问题使用递归。

此方法还应检查包含对象的数组或列表:

public static bool FieldsEquals(this object o1, object o2)
{
    if (ReferenceEquals(o1, o2))
        return true;

    if (o1 == null || o2 == null || o1.GetType() != o2.GetType())
        return false;

    if (o1 is IEnumerable enumerable1 && o2 is IEnumerable enumerable2)
    {
        var enumerator1 = enumerable1.GetEnumerator();
        var enumerator2 = enumerable2.GetEnumerator();

        while(enumerator1.MoveNext())
        {
            if (!enumerator2.MoveNext())
            {
                return false;
            }

            if (!enumerator1.Current.FieldsEquals(enumerator2.Current))
            {
                return false;
            }
        }
    }
    else
    {
        foreach (var f in o1.GetType().GetFields())
        {
            var val1 = f.GetValue(o1);
            var val2 = f.GetValue(o2);

            if (val1 == null || val2 == null) continue;
            if (val1 is IEnumerable e1 && val2 is IEnumerable e2)
            {
                if (!e1.FieldsEquals(e2))
                {
                    return false;
                }
            }
            else
            {
                if (!val1.Equals(val2))
                {
                    return false;
                }
            }
        }
    }

    return true;
}

【讨论】:

  • 您需要在 while 循环之后进行以下检查: if (enumerator2.MoveNext()) { return false; }。如果你不这样做,那么当 enumerable1 为空而 enumerable2 有一些项目时,你会得到不正确的结果。
  • 另外,我会让这个方法通用,这样我就可以提前获得一些类型信息。我认为以下内容在一般意义上是不正确的:if (o1 == null || o2 == null || o1.GetType() != o2.GetType()) return false;。对我和 C# 语言来说,如果 obj1 == null && obj2 == null,那么这些对象是相等的,即 obj1 == obj2 为真。
【解决方案2】:

说实话,这里有很多问题。如果类型是基本类型(如 int)或结构(日期时间等)怎么办。如果可枚举字段包含类而不是基本类型怎么办?还是属性是类?

有关深度平等的一些准则,请参阅此问题:Compare the content of two objects for equality

说了这么多,这是我对你提到的代码的破解

public static bool FieldsEquals(this object o, object other)
{
    if (ReferenceEquals(o, other)) return true;
    if (o == null || other == null || o.GetType() != other.GetType()) return false;

    foreach (var f in o.GetType().GetFields())
    {
        bool isEnumerable = f.GetValue(o).GetType().IsAssignableFrom(typeof(System.Collections.IEnumerable));// but is not a string
        if (!isEnumerable)
        {
            if (!f.GetValue(o).Equals(f.GetValue(other))) return false;

        }
        else
        {
            var first = ((System.Collections.IEnumerable)f.GetValue(o)).Cast<object>().ToArray();
            var second = ((System.Collections.IEnumerable)f.GetValue(other)).Cast<object>().ToArray();
            if (first.Length != second.Length)
                return false;
            for (int i = 0; i < first.Length; i++)
            {
                if (first[i] != second[i]) //assumes they are basic types, which implement equality checking. If they are classes, you may need to recursively call this method
                    return false;
            }
        }
    }
    return true;
}

【讨论】:

  • 谢谢,我不是在寻找深度相等,我假设所有基本类型或对象(不是集合)的字段都知道如何相等,然后你会解决吗?
  • 还有没有办法使用 sequenceEqual ??
  • SequenceEqual 没问题,但如果数组包含对象,那么它将失败。您需要在 for 循环中添加代码以测试“对象”数组(基本上重新调整此方法,以便如果它不是基本类型、字符串或日期时间,则递归该方法)
猜你喜欢
  • 2019-03-17
  • 2019-04-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多