Словари "глубокого сравнения" - PullRequest
1 голос
/ 18 января 2012

Я делаю "глубокое сравнение" структур данных для ведения журнала аудита (кто менял членов, когда). Я использую отражение для этого, возвращаясь к структурам и сравнивая их. Я столкнулся с проблемой, хотя с теми, которые содержат словари.

Я могу определить, что участник является словарем по typeof(IDictionary).IsAssignableFrom(memberType). Мой план состоит в том, чтобы собрать ключи, которые присутствуют в обоих объектах, и продолжить рекурсию по ним. Однако IDictionary.Keys - это ICollection, который не распространяется LINQ. Не зная типа клавиш, как мне этого добиться?

Может быть, этот метод неоптимален (я не настолько опытен в комбо Generics / Reflection), я должен сделать это другим способом?

Ответы [ 2 ]

1 голос
/ 18 января 2012

Это поможет вам с вашими итерациями отражения.

IDictionary<int, string> t;

bool t.GetType().IsGenericType
Type[] t.GetType().GetGenericArguments() 
// you can do foreach here and see again if type is generic

Вы можете создать вспомогательный метод, в котором вы сначала проверяете, является ли тип универсальным, а затем проверяет универсальные типы аргументов. Это позволит проверить не только общие словари, но и любой тип, имеющий общие аргументы. IList, KeyValuePair и т. Д.

public static bool IsType(Type inputType, Type targetType)
{
    if (inputType.IsGenericType)
    {
        Type[] genericArgs = inputType.GetGenericArguments();
        var foundType = false;
        foreach (var item in genericArgs)
        {
            if (IsType(item, targetType))
                foundType = true;
        }
        return foundType;
    }
    return inputType.IsAssignableFrom(targetType);
}
0 голосов
/ 19 января 2012

Нашел решение сам. Здесь ChangedProperties - это тип, содержащий имя свойства и значения до / после изменения.

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