扩展方法比较所有字段,包括IEnumerable

时间:2018-06-29 04:44:35

标签: c# c#-7.0

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

我有这个:

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]相同。

有帮助吗?

2 个答案:

答案 0 :(得分:2)

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

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

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

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

答案 1 :(得分:1)

说实话,这里有很多问题。如果类型是基本类型(例如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;
}