2012-01-18 76 views
1

我正在为审计日志记录(谁更改什么成员,何时)进行“深度比较”。我正在使用反射,递归到结构中并进行比较。尽管包含字典的人遇到了问题。“深度比较”字典

我可以通过typeof(IDictionary).IsAssignableFrom(memberType)检测到成员是字典。然后,我的计划是收集这两个对象中存在的密钥,并继续对这些对象进行递归。但是,IDictionary.Keys是一个ICollection,它不被LINQ扩展。不知道钥匙的类型,我怎么能做到这一点?

也许这种方法不是最佳的(我不是那种在泛型/反射组合中经验丰富的),我应该以另一种方式做到这一点吗?

+0

你的问题与LINQ有什么关系? – Seb 2012-01-18 15:19:13

+0

@DavidM:只有模板版本,不是'IEnumerable'。在我的问题中说'memberType'将会是'Dictionary '。这是可以分配给'IDictionary',但据我所知,不可能做'IDictionary ',所以我卡住了与非泛型的'IDictionary'。 – carlpett 2012-01-19 08:22:14

回答

0

自己找到了解决办法。这里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; 
} 
1

这将帮助您反思迭代。

IDictionary<int, string> t; 

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

您可以创建一个帮助器方法,首先测试type是否为泛型,然后检查泛型参数类型。这将不仅测试通用字典,而且测试任何具有通用参数的类型。 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); 
}