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
(附加实体)并对此方法(注释行)进行递归调用。
问题似乎是self
和other
在递归调用时输入到object
,因此primitive
得到零结果。如果我在第一级检查self
和this
上的类型,我会得到实际类型,但在第二级我会得到object
。我尝试过使用Convert.ChangeType
,但没有用。
答案 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();
}
}