递归调用比较对象属性

时间:2013-11-13 08:53:45

标签: c# linq recursion

public static bool PropertiesEqual<T>(this T self, T other, string[] skip)
{
    if (self.Equals(other)) return true;

        var primitive = (from p in typeof(T).GetProperties() 
                         where !skip.Contains(p.Name) 
                         && p.PropertyType.IsSimpleType() 
                         select p).ToList();

        var rest = (from p in typeof(T).GetProperties()
                      where !p.PropertyType.IsSimpleType() select p).ToList();

        foreach(var pi in rest)
        {
            var selfValue = pi.GetValue(self, null);
            var otherValue = pi.GetValue(other, null);

            //var result = selfValue.PropertiesEqual(otherValue);

            if (!object.Equals(selfValue, otherValue))
                return false;
        }


        foreach (var pi in primitive)
        {
            var selfValue = pi.GetValue(self, null);
            var otherValue = pi.GetValue(other, null);

            return object.Equals(selfValue, otherValue);
        }

        return true;
}

public static bool IsSimpleType(this Type type)
{
    return (type.IsValueType || type.IsPrimitive || 
       type == typeof(String) || Convert.GetTypeCode(type) != TypeCode.Object);
}

我正在使用此方法来比较实体实例上的相等性。在第一级它工作得很好,但我想迭代rest(附加实体)并对此方法(注释行)进行递归调用。

问题似乎是selfother在递归调用时输入到object,因此primitive得到零结果。如果我在第一级检查selfthis上的类型,我会得到实际类型,但在第二级我会得到object。我尝试过使用Convert.ChangeType,但没有用。

3 个答案:

答案 0 :(得分:2)

没有理由让它具有通用性。而不是typeof(T)使用self.GetType() / other.GetType()来检索正确的运行时类型。

答案 1 :(得分:1)

如果您更改自我和其他值分配会怎样?

var selfValue = Convert.ChangeType(pi.GetValue(self, null), pi.PropertyType);
var otherValue = Convert.ChangeType(pi.GetValue(other, null), pi.PropertyType);

我注意到虽然T成为对象,但是自我和其他都是正确的类型。因此,不要使用typeof(T)中的gettign yoru属性,请尝试:

var rest = (from p in self.GetType().GetProperties() select p).ToList();

在我的简单测试中,它工作并返回了所需的道具。 您可能需要更改多于这一行:)

答案 2 :(得分:0)

回过头来,我做了一个comparerhelper,它允许排除属性。 也许代码可以帮助你:

public class CompareHelper
        {

            Hashtable reccorido = new Hashtable();
            List<IExcludeProperties> excludeProperties = new List<IExcludeProperties>();
            private readonly List<string> genericListPropertiesNames = new List<string>() { "Count", "Capacity", "Item" };
            public CompareHelper():this(new List<IExcludeProperties>())
            {

            }

            public CompareHelper(List<IExcludeProperties> excludeProperties)
            {
                this.excludeProperties = excludeProperties;
            }

            public bool AreEquals<T1, T2>(T1 value1, T2 value2)
            {
                try
                {
                    reccorido = new Hashtable();
                    return Compare(value1, value2);
                }
                catch (NotEqualsException ex)
                {
                    PropertyFail = ex.Where();
                    return false;
                }
            }

            public string PropertyFail
            {
                get;
                private set;
            }

            private bool Compare<T1, T2>(T1 value1, T2 value2)
            {
                if (value1 == null && value2 == null)
                {
                    return true;
                }
                if ((value1 == null) || (value2 == null))
                {
                    throw new NotEqualsException(value1, value2);
                    //return false;
                }

                string key = GetKey<T1, T2>(value1, value2);

                if (reccorido.Contains(key))
                {
                    return true;
                }

                reccorido.Add(key, true);
                Type tipo1 = GetType(value1.GetType());
                Type tipo2 = GetType(value2.GetType());


                if (tipo1 != tipo2)
                {
                    throw new NotEqualsException(value1, value2);
                    //  return false;
                }


                if (IsBasicCompare(tipo1))
                {
                    return CompareBasic(ConvertTo(value1, tipo1), ConvertTo(value2, tipo1));

                }
                dynamic v1 = ConvertTo(value1, tipo1);
                dynamic v2 = ConvertTo(value2, tipo1);
                if (!CompareFields(v1, v2))
                {

                    throw new NotEqualsException(value1, value2);
                    //return false;
                }


                return CompareProperties(v1, v2);
            }

            private string GetKey<T1, T2>(T1 value1, T2 value2)
            {

                int hascodeA = value1.GetHashCode();
                int hascodeB = value2.GetHashCode();
                if (hascodeA > hascodeB)
                    return string.Format("{0}{1}", hascodeA, hascodeB);

                return string.Format("{0}{1}", hascodeB, hascodeA);
            }

            private dynamic ConvertTo(object value1, Type t)
            {
                if (value1 == null)
                    return null;
                return Convert.ChangeType(value1, GetType(t));
            }

            private bool CompareProperties<T>(T value1, T value2)
            {


                if (IsGenericList(typeof(T)))
                {
                    return ComparareGenericList(value1, value2);

                }
                List<PropertyInfo> properties = GetPropertiesToCheck<T>();
                foreach (var p in properties)
                {


                    try
                    {
                        var valueA = p.GetValue(value1, null);
                        var valueB = p.GetValue(value2, null);

                        if (!(valueA == null && valueB == null))
                        {
                            if (valueA == null || valueB == null)
                            {
                                throw new NotEqualsException(value1, value2);
                                // return false;
                            }

                            if (IsBasicCompare(p.PropertyType))
                            {
                                valueA = ConvertTo(p.GetValue(value1, null), p.PropertyType);
                                valueB = ConvertTo(p.GetValue(value2, null), p.PropertyType);


                                if (!CompareBasic(valueA, valueB))
                                {
                                    throw new NotEqualsException(value1, value2);

                                    // return false;
                                }

                            }
                            else if (IsEnumerable(p.PropertyType))
                            {

                                if (!CompareAsInumerable(valueA, valueB))
                                {
                                    throw new NotEqualsException(value1, value2);
                                    //   return false;
                                }
                            }
                            else if (p.PropertyType.IsClass)
                            {
                                if (!Compare(ConvertTo(p.GetValue(value1, null), p.PropertyType), ConvertTo(p.GetValue(value2, null), p.PropertyType)))
                                {
                                    throw new NotEqualsException(value1, value2);
                                }
                            }
                            else
                                throw new Exception(string.Format("Tipo no especificado {0}", p.PropertyType));
                        }

                    }

                    catch (NotEqualsException ex)
                    {
                        ex.AddParent(p.Name);
                        throw;
                    }

                }
                return true;


            }

            private  List<PropertyInfo> GetPropertiesToCheck<T>()
            {

                List<PropertyInfo> properties = new List<PropertyInfo>();

                Type typeToCheck= typeof(T);
                IExcludeProperties exclude=excludeProperties.FirstOrDefault(excl=>excl.ExcludeType().IsAssignableFrom(typeToCheck));
                if(exclude!=null)
                    return typeToCheck.GetProperties().Where(p => p.CanRead && (!exclude.GetPropertiesNames().Any(n=>n==p.Name))).ToList();

               // 
                return typeToCheck.GetProperties().Where(p => p.CanRead).ToList();


            }

            private bool ComparareGenericList<T>(T value1, T value2)
            {

                List<PropertyInfo> properties = typeof(T).GetProperties().Where(p => p.CanRead && p.Name != "Capacity").ToList(); //la capacidad no la compruebo!!


                PropertyInfo count = typeof(T).GetProperty("Count");

                int totalA = ConvertTo(count.GetValue(value1, null), count.PropertyType);
                int totalB = ConvertTo(count.GetValue(value2, null), count.PropertyType);

                if (!Compare(totalA, totalB))
                    return false;


                PropertyInfo item = typeof(T).GetProperty("Item");
                CompareAsInumerable(value1, value2);

                return true;

            }

            private bool IsGenericList(Type t)
            {


                return t.IsGenericType && IsEnumerable(t) && t.GetProperties().Where(p => p.CanRead).Any(p => genericListPropertiesNames.Contains(p.Name));

            }
            [Conditional("DEBUG")]
            private void ShowInfo(PropertyInfo p)
            {
                Debug.WriteLine(string.Format("Checkeando propiedad {0}",p.Name));
            }

            private bool CompareFields<T>(T value1, T value2)
            {
                List<FieldInfo> fields = typeof(T).GetFields().Where(f => f.IsPublic).ToList();

                foreach (var f in fields)
                {
                    dynamic valueA = f.GetValue(value1);
                    dynamic valueB = f.GetValue(value2);

                    if (!Compare(f.GetValue(value1), f.GetValue(value2)))
                    {
                        throw new NotEqualsException(value1, value2);
                        //return false;
                    }

                }
                return true;
            }


            private bool CompareAsInumerable<T>(T valueA, T valueB)
            {
                IEnumerable<object> colA = ((IEnumerable)valueA).Cast<object>();
                IEnumerable<object> colB = ((IEnumerable)valueB).Cast<object>();
                if (colA.Count() != colB.Count())
                    return false;

                Type t1 = GetType(colA.GetType());
                Type t2 = GetType(colB.GetType());

                if (t1 != t2)
                    return false;

                if (colA.Count() > 0)
                {
                    Type itemType = GetTypeOfItem(colA);

                    for (int i = 0; i < colA.Count(); i++)
                    {
                        try
                        {
                            dynamic a = colA.ElementAt(i);
                            dynamic b = colB.ElementAt(i);

                            if (!Compare(a, b))
                            {
                                throw new NotEqualsException(colA.ElementAt(i), colB.ElementAt(i));
                                //return false;
                            }
                        }
                        catch (NotEqualsException ex)
                        {
                            ex.AddParent(itemType.Name);
                           throw ;
                        }
                    }
                }
                return true;
            }

            private Type GetTypeOfItem(IEnumerable<object> collection)
            {

                if (collection == null)
                    return null;
                Type[] t = collection.GetType().GetGenericArguments();

                if ((t != null) && (t.Count() > 0))
                    return t[0];
                return null;

            }



            private bool IsEnumerable(Type type)
            {
                return typeof(IEnumerable).IsAssignableFrom(type);
            }

            private bool CompareBasic<T>(T valueA, T valueB)
            {
                bool result;
                IComparable selfValueComparer;

                selfValueComparer = valueA as IComparable;

                if (valueA == null && valueB != null || valueA != null && valueB == null)
                    result = false;
                else if (selfValueComparer != null && selfValueComparer.CompareTo(valueB) != 0)
                    result = false;
                else if (!object.Equals(valueA, valueB))
                    result = false;
                else
                    result = true;

                if (!result)
                    throw new NotEqualsException(valueA, valueB);
                return result;


            }
            private bool IsBasicCompare(Type type)
            {
                return typeof(IComparable).IsAssignableFrom(type) || type.IsPrimitive || type.IsValueType;
            }


            private Type GetType<T>()
            {
                return GetType(typeof(T));
            }
            private Type GetType(Type t)
            {
                Type tipo = Nullable.GetUnderlyingType(t);
                if (tipo == null)
                    tipo = t;
                return (tipo == null) ? t : tipo;
            }


        }

助手类:

 public interface IExcludeProperties
    {
        Type ExcludeType();
       void  AddPropertyName(string propertyName);
        List<string> GetPropertiesNames();
    }

  public class ExcludeProperties<T> : IExcludeProperties
    {
        HashSet<string> propertiesNames = new HashSet<string>();

        List<PropertyInfo> props = new List<PropertyInfo>();


        public ExcludeProperties()
        {
            props = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly).ToList();
        }

        public Type ExcludeType()
        {
            return typeof(T);
        }

        public void AddPropertyName(string propertyName)
        {
            if(! typeof(T).IsAbstract &&  !props.Any(p=>p.Name==propertyName) )
                throw new Exception(string.Format("No existe y no por lo tanto no se puede excluir la propiedad {0} para el tipo {1}!",propertyName,typeof(T).Name));


              propertiesNames.Add(propertyName);
        }

    public List<string> GetPropertiesNames()
    {
        return propertiesNames.ToList();
    }
}