patterncsharpMinor
Generic equality checker
Viewed 0 times
genericequalitychecker
Problem
I use this method to check if two reference types are equal
I use attributes on properties/fields to find which of them are used to decide if the two objects are equals ex :
It works very well but I am bothered with how I get the properties and fields in a list and use reflection to invoke the
public static bool AreEquals(T source, object obj) where T : class
{
if (ReferenceEquals(source, obj))
return true;
var convertedTarget = obj as T;
if (ReferenceEquals(convertedTarget, null))
return false;
List equalityMembers = typeof (T).GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).Where(p => p.GetCustomAttribute() != null).ToList();
equalityMembers.AddRange(typeof(T).GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).Where(p => p.GetCustomAttribute() != null).ToList());
if (equalityMembers.Count == 0)
return false; //The references were not equals and there is nothing to compare..
var enumerator = equalityMembers.GetEnumerator();
bool areEquals = true;
while (enumerator.MoveNext() && areEquals)
{
var current = enumerator.Current;
//On sait que FieldInfo et PropertyInfo ont la méthode GetValue
var methodInfo = current.GetType().GetMethod("GetValue", new [] {typeof (object)});
object valueSource = methodInfo.Invoke(current, new object[] {source});
object valueObj = methodInfo.Invoke(current, new object[] {convertedTarget});
areEquals = valueSource.Equals(valueObj);
}
return areEquals;
}I use attributes on properties/fields to find which of them are used to decide if the two objects are equals ex :
public class MyClass
{
[EqualityMember]
public int ID{get;set;}
public override Equals(object obj)
{
return MyStaticClass.AreEquals(this,obj);
}
}It works very well but I am bothered with how I get the properties and fields in a list and use reflection to invoke the
GetValue method, would you have any other alternatives? Or is there anything that seems flawed in my method?Solution
I took a shot at this as well. I think @Hangy had a few good ideas but ended up with a rather complex solution. I haven't added any caching so you might want to add that yourself afterwards.
What I changed:
The end result:
What I changed:
- Different check for
nullwhich is clearer in my opinion.
- Braces for one-line statements.
- Extracted member parsing to a separate method.
- Instead of holding all data of
PropertyInfoandFieldInfo, I simply use aDictionarywhich translates to `.
- Unused properties
andfieldsdata is eligible for GC as soon as you extracted the needed information.
- Got rid of the Enumerator` and added some juicy LINQ.
The end result:
public static class EqualityAttribute
{
public static bool AreEquals(T source, object obj) where T : class
{
if (ReferenceEquals(source, obj))
{
return true;
}
var convertedTarget = obj as T;
if (convertedTarget == null)
{
return false;
}
var obj1Members = GetFieldEqualityMembers(source);
var obj2Members = GetFieldEqualityMembers(obj);
var noMembersToCompare = obj1Members.Values.Count == 0 && obj2Members.Values.Count == 0;
if (noMembersToCompare)
{
return false;
}
if (obj1Members.Keys.Any(key => !obj1Members[key].Equals(obj2Members[key])))
{
return false;
}
return true;
}
private static Dictionary GetFieldEqualityMembers(T obj)
{
var properties = obj.GetType().GetProperties(BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance)
.Where(x => x.GetCustomAttribute() != null)
.ToDictionary(x => x.Name, x => x.GetValue(obj));
var fields = obj.GetType().GetFields(BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance)
.Where(x => x.GetCustomAttribute() != null)
.ToDictionary(x => x.Name, x => x.GetValue(obj));
properties.ToList().ForEach(x => fields.Add(x.Key, x.Value));
return fields;
}
}Code Snippets
public static class EqualityAttribute
{
public static bool AreEquals<T>(T source, object obj) where T : class
{
if (ReferenceEquals(source, obj))
{
return true;
}
var convertedTarget = obj as T;
if (convertedTarget == null)
{
return false;
}
var obj1Members = GetFieldEqualityMembers(source);
var obj2Members = GetFieldEqualityMembers(obj);
var noMembersToCompare = obj1Members.Values.Count == 0 && obj2Members.Values.Count == 0;
if (noMembersToCompare)
{
return false;
}
if (obj1Members.Keys.Any(key => !obj1Members[key].Equals(obj2Members[key])))
{
return false;
}
return true;
}
private static Dictionary<string, object> GetFieldEqualityMembers<T>(T obj)
{
var properties = obj.GetType().GetProperties(BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance)
.Where(x => x.GetCustomAttribute<EqualityMemberAttribute>() != null)
.ToDictionary(x => x.Name, x => x.GetValue(obj));
var fields = obj.GetType().GetFields(BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance)
.Where(x => x.GetCustomAttribute<EqualityMemberAttribute>() != null)
.ToDictionary(x => x.Name, x => x.GetValue(obj));
properties.ToList().ForEach(x => fields.Add(x.Key, x.Value));
return fields;
}
}Context
StackExchange Code Review Q#60054, answer score: 5
Revisions (0)
No revisions yet.