I'm doing "deep comparison" of data structures for audit logging (who changed what members, when). I'm using reflection for this, recursing into the structures and comparing them. I've hit a problem though with those containing dictionaries.
I can detect that a member is a dictionary by typeof(IDictionary).IsAssignableFrom(memberType)
. My plan is then to collect the keys which are present in both objects, and continue recursion on those. However, IDictionary.Keys
is an ICollection
, which is not extended by LINQ. Not knowing the type of the keys, how could I achieve this?
Maybe this method is suboptimal (I'm not that experienced in the Generics/Reflection combo), should I do this in another way?
Found a solution myself. Here ChangedProperties
is a type containing property name, and values pre/post change.
if (typeof(IDictionary).IsAssignableFrom(propertyType))
{
Type keyType = propertyType.GetGenericArguments()[0],
valueType = propertyType.GetGenericArguments()[1];
Type hashsetType = typeof(HashSet<>).MakeGenericType(keyType);
var hashsetCtor = hashsetType.GetConstructor(new[] { typeof(IEnumerable<>).MakeGenericType(keyType) });
dynamic aDict = a;
dynamic bDict = b;
dynamic aKeys = hashsetCtor.Invoke(new object[] { aDict.Keys });
dynamic bKeys = hashsetCtor.Invoke(new object[] { bDict.Keys });
List<ChangedProperty> changes = new List<ChangedProperty>();
foreach (var key in Enumerable.Intersect(aKeys, bKeys))
{
// Present in both dictionaries. Recurse further
}
foreach (var key in Enumerable.Except(aKeys, bKeys))
{
// Key was removed
}
foreach (var key in Enumerable.Except(bKeys, aKeys))
{
// Key was added
}
return changes;
}